From: Otto Moerbeek Date: Tue, 16 Jan 2024 11:35:50 +0000 (+0100) Subject: YAML defs for all Lua config constructs, plus converting old-style to YAML X-Git-Tag: rec-5.1.0-alpha1~9^2~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bee74d905a69fd6452731f9ef6e4e2754089b352;p=thirdparty%2Fpdns.git YAML defs for all Lua config constructs, plus converting old-style to YAML --- diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 788f3ccb57..cd77ab8d20 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1399,6 +1399,7 @@ Toshifumi Travaille tribool trustanchor +trustanchorfile trusteer trx trxid @@ -1567,6 +1568,7 @@ zonemd zonemetadata zonename zoneparser +zonetocaches zonetransfer Zonneveld zsk diff --git a/pdns/recursordist/rec-lua-conf.cc b/pdns/recursordist/rec-lua-conf.cc index bd080360f5..ff204c3cab 100644 --- a/pdns/recursordist/rec-lua-conf.cc +++ b/pdns/recursordist/rec-lua-conf.cc @@ -380,6 +380,7 @@ static void rpzPrimary(LuaConfigItems& lci, luaConfigDelayedThreads& delayedThre zone->clear(); } } + lci.rpzRaw.emplace_back(RPZRaw{primaries, zoneName, options}); } catch (const std::exception& e) { SLOG(g_log << Logger::Error << "Problem configuring 'rpzPrimary': " << e.what() << endl, @@ -472,6 +473,7 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de lci.dfe.addZone(std::move(zone)); SLOG(g_log << Logger::Warning << "Done loading RPZ from file '" << filename << "'" << endl, log->info(Logr::Info, "Done loading RPZ from file")); + lci.rpzRaw.emplace_back(RPZRaw{{}, filename, options}); } catch (const std::exception& e) { SLOG(g_log << Logger::Error << "Unable to load RPZ zone from '" << filename << "': " << e.what() << endl, diff --git a/pdns/recursordist/rec-lua-conf.hh b/pdns/recursordist/rec-lua-conf.hh index e278acd108..abed5c4196 100644 --- a/pdns/recursordist/rec-lua-conf.hh +++ b/pdns/recursordist/rec-lua-conf.hh @@ -21,6 +21,7 @@ */ #pragma once #include +#include #include "sholder.hh" #include "sortlist.hh" @@ -98,12 +99,22 @@ struct ProxyByTableValue using ProxyMapping = NetmaskTree; +using rpzOptions_t = std::unordered_map>>>; + +struct RPZRaw +{ + std::vector addresses; + std::string name; + boost::optional options; +}; + class LuaConfigItems { public: LuaConfigItems(); SortList sortlist; DNSFilterEngine dfe; + vector rpzRaw; TrustAnchorFileInfo trustAnchorFileInfo; // Used to update the Trust Anchors from file periodically map dsAnchors; map negAnchors; diff --git a/pdns/recursordist/rec-main.cc b/pdns/recursordist/rec-main.cc index b441f3acc9..2bfc587aa8 100644 --- a/pdns/recursordist/rec-main.cc +++ b/pdns/recursordist/rec-main.cc @@ -2905,8 +2905,8 @@ static pair doYamlConfig(Logr::log_t startupLog, int argc, char* argv pdns::rust::settings::rec::Recursorsettings settings; pdns::settings::rec::oldStyleSettingsToBridgeStruct(settings); luaConfigDelayedThreads delayedLuaThreads; + ProxyMapping proxyMapping; try { - ProxyMapping proxyMapping; loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping); } catch (PDNSException& e) { @@ -2914,21 +2914,7 @@ static pair doYamlConfig(Logr::log_t startupLog, int argc, char* argv startupLog->error(Logr::Error, e.reason, "Cannot load Lua configuration")); } auto luaConfig = g_luaconfs.getLocal(); - settings.dnssec.trustanchorfile = luaConfig->trustAnchorFileInfo.fname; - settings.dnssec.trustanchorfile_interval = luaConfig->trustAnchorFileInfo.interval; - for (const auto& anchors : luaConfig->dsAnchors) { - rust::Vec dsRecords; - for (const auto& dsRecord : anchors.second) { - dsRecords.emplace_back(dsRecord.getZoneRepresentation()); - } - pdns::rust::settings::rec::TrustAnchor trustAnchor{anchors.first.toString(), dsRecords}; - settings.dnssec.trustanchors.emplace_back(trustAnchor); - } - for (const auto& anchors : luaConfig->negAnchors) { - pdns::rust::settings::rec::NegativeTrustAnchor negtrustAnchor{anchors.first.toString(), anchors.second}; - settings.dnssec.negative_trustanchors.emplace_back(negtrustAnchor); - } - + pdns::settings::rec::fromLuaConfigToBridgeStruct(luaConfig, proxyMapping, settings); auto yaml = settings.to_yaml_string(); cout << yaml << endl; } diff --git a/pdns/recursordist/settings/cxxsettings.hh b/pdns/recursordist/settings/cxxsettings.hh index ad21578da1..8b4993bf37 100644 --- a/pdns/recursordist/settings/cxxsettings.hh +++ b/pdns/recursordist/settings/cxxsettings.hh @@ -24,7 +24,9 @@ #include #include "rust/cxx.h" #include "rust/lib.rs.h" +#include "sholder.hh" #include "logging.hh" +#include "rec-lua-conf.hh" namespace pdns::settings::rec { @@ -50,4 +52,5 @@ void readYamlAllowFromFile(const std::string& filename, ::rust::Vec<::rust::Stri void readYamlAllowNotifyForFile(const std::string& filename, ::rust::Vec<::rust::String>& vec, Logr::log_t log); void setArgsForZoneRelatedSettings(pdns::rust::settings::rec::Recursorsettings& settings); void setArgsForACLRelatedSettings(pdns::rust::settings::rec::Recursorsettings& settings); +void fromLuaConfigToBridgeStruct(LocalStateHolder& luaConfig, const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Recursorsettings& settings); } diff --git a/pdns/recursordist/settings/cxxsupport.cc b/pdns/recursordist/settings/cxxsupport.cc index 9c690a0bff..348cddd128 100644 --- a/pdns/recursordist/settings/cxxsupport.cc +++ b/pdns/recursordist/settings/cxxsupport.cc @@ -33,6 +33,7 @@ #include "cxxsettings-private.hh" #include "logger.hh" #include "logging.hh" +#include "rec-lua-conf.hh" ::rust::Vec<::rust::String> pdns::settings::rec::getStrings(const std::string& name) { @@ -518,7 +519,7 @@ static void processLine(const std::string& arg, FieldMap& map, bool mainFile) ::rust::String section; ::rust::String fieldname; ::rust::String type_name; - pdns::rust::settings::rec::Value rustvalue = {false, 0, 0.0, "", {}, {}, {}, {}, {}}; + pdns::rust::settings::rec::Value rustvalue = {false, 0, 0.0, "", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}; if (pdns::settings::rec::oldKVToBridgeStruct(var, val, section, fieldname, type_name, rustvalue)) { auto overriding = !mainFile && !incremental && !simpleRustType(type_name); auto [existing, inserted] = map.emplace(std::pair{std::pair{section, fieldname}, pdns::rust::settings::rec::OldStyle{section, fieldname, var, std::move(type_name), rustvalue, overriding}}); @@ -618,13 +619,34 @@ std::string pdns::settings::rec::defaultsToYaml() ::rust::String section; ::rust::String fieldname; ::rust::String type_name; - pdns::rust::settings::rec::Value rustvalue{false, 0, 0.0, "", {}, {}, {}, {}, {}}; + pdns::rust::settings::rec::Value rustvalue{false, 0, 0.0, "", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}; string name = var; string val = arg().getDefault(var); if (pdns::settings::rec::oldKVToBridgeStruct(name, val, section, fieldname, type_name, rustvalue)) { map.emplace(std::pair{std::pair{section, fieldname}, pdns::rust::settings::rec::OldStyle{section, fieldname, name, std::move(type_name), std::move(rustvalue), false}}); } } + + // Should be generated + auto def = [&](const string& section, const string& name, const string& type) { + pdns::rust::settings::rec::Value rustvalue{.vec_trustanchor_val = {}, .vec_negativetrustanchor_val = {}, .string_val = "", .u64_val = 24, .vec_protobufserver_val = {}}; + map.emplace(std::pair{std::pair{section, name}, pdns::rust::settings::rec::OldStyle{section, name, name, type, rustvalue, false}}); + }; + def("dnssec", "trustanchors", "Vec"); + def("dnssec", "negative_trustanchors", "Vec"); + def("dnssec", "trustanchorfile", "String"); + def("dnssec", "trustanchorfile_interval", "u64"); + def("logging", "protobuf_servers", "Vec"); + def("logging", "outgoing_protobuf_servers", "Vec"); + def("logging", "dnstap_framestream_servers", "Vec"); + def("logging", "dnstap_nod_framestream_servers", "Vec"); + def("recursor", "rpzs", "Vec"); + def("recursor", "sortlists", "Vec"); + def("recordcache", "zonetocaches", "Vec"); + def("recursor", "allowed_additional_qtypes", "Vec"); + def("incoming", "proxymappings", "Vec"); + // End of should be generated XXX + // Convert the map to a vector, as CXX does not have any dictionary like support. ::rust::Vec vec; vec.reserve(map.size()); @@ -682,3 +704,246 @@ std::string pdns::settings::rec::defaultsToYaml() } return res; } + +namespace +{ +void fromLuaToProtobufServerStruct(const ProtobufExportConfig& pbConfig, pdns::rust::settings::rec::ProtobufServer& pbServer) +{ + for (const auto& server : pbConfig.servers) { + pbServer.servers.emplace_back(server.toStringWithPort()); + } + pbServer.timeout = pbConfig.timeout; + pbServer.maxQueuedEntries = pbConfig.maxQueuedEntries; + pbServer.reconnectWaitTime = pbConfig.reconnectWaitTime; + pbServer.taggedOnly = pbConfig.taggedOnly; + pbServer.asyncConnect = pbConfig.asyncConnect; + pbServer.logQueries = pbConfig.logQueries; + pbServer.logResponses = pbConfig.logResponses; + for (const auto num : pbConfig.exportTypes) { + pbServer.exportTypes.emplace_back(QType(num).toString()); + } + pbServer.logMappedFrom = pbConfig.logMappedFrom; +} + +void fromLuaConfigToTAInfo(LocalStateHolder& luaConfig, pdns::rust::settings::rec::Dnssec& dnssec) +{ + dnssec.trustanchorfile = luaConfig->trustAnchorFileInfo.fname; + dnssec.trustanchorfile_interval = luaConfig->trustAnchorFileInfo.interval; + for (const auto& anchors : luaConfig->dsAnchors) { + ::rust::Vec<::rust::String> dsRecords; + for (const auto& dsRecord : anchors.second) { + dsRecords.emplace_back(dsRecord.getZoneRepresentation()); + } + pdns::rust::settings::rec::TrustAnchor trustAnchor{anchors.first.toString(), dsRecords}; + dnssec.trustanchors.emplace_back(trustAnchor); + } + for (const auto& anchors : luaConfig->negAnchors) { + pdns::rust::settings::rec::NegativeTrustAnchor negtrustAnchor{anchors.first.toString(), anchors.second}; + dnssec.negative_trustanchors.emplace_back(negtrustAnchor); + } +} + +template +void setIfAvailable(const rpzOptions_t& table, T& var, const std::string& name) +{ + if (table.count(name) != 0) { + var = boost::get(table.at(name)); + } +} + +void setIfAvailable(const rpzOptions_t& table, rust::string& var, const std::string& name) +{ + if (table.count(name) != 0) { + var = boost::get(table.at(name)); + } +} + +void setIfAvailable(const rpzOptions_t& table, pdns::rust::settings::rec::TSIGTriplet& var, const std::string& name) +{ + if (table.count(name) != 0) { + var.name = boost::get(table.at("tsigname")); + var.algo = boost::get(table.at("tsigalgo")); + var.secret = boost::get(table.at("tsigsecret")); + } +} +void fromLuaConfigToRPZ(const vector& rpzs, pdns::rust::settings::rec::Recursor& rec) +{ + for (const auto& rpz : rpzs) { + pdns::rust::settings::rec::RPZ rustrpz{ + .defpolOverrideLocalData = true, + .defttl = std::numeric_limits::max(), + .extendedErrorCode = 0, + .includeSOA = false, + .ignoreDuplicates = false, + .maxTTL = std::numeric_limits::max(), + .overridesGettag = true, + .zoneSizeHint = 0, + .refresh = 0, + .maxReceivedMBytes = 0, + .axfrTimeout = 20}; + + for (const auto& address : rpz.addresses) { + rustrpz.addresses.emplace_back(address.toStringWithPort()); + } + rustrpz.name = rpz.name; + if (rpz.options) { + setIfAvailable(*rpz.options, rustrpz.defcontent, "defcontent"); + setIfAvailable(*rpz.options, rustrpz.defpol, "defpol"); + setIfAvailable(*rpz.options, rustrpz.defpolOverrideLocalData, "defpolOverrideLocalData"); + setIfAvailable(*rpz.options, rustrpz.defttl, "defttl"); + setIfAvailable(*rpz.options, rustrpz.extendedErrorCode, "extendedErrorCode"); + setIfAvailable(*rpz.options, rustrpz.extendedErrorExtra, "extendedErrorExtra"); + setIfAvailable(*rpz.options, rustrpz.includeSOA, "includeSOA"); + setIfAvailable(*rpz.options, rustrpz.ignoreDuplicates, "ignoreDuplicates"); + setIfAvailable(*rpz.options, rustrpz.maxTTL, "maxTTL"); + setIfAvailable(*rpz.options, rustrpz.policyName, "policyName"); + if (rpz.options->count("tags") != 0) { + const auto& tags = boost::get>>(rpz.options->at("have")); + for (const auto& tag : tags) { + rustrpz.tags.emplace_back(tag.second); + } + } + setIfAvailable(*rpz.options, rustrpz.overridesGettag, "overridesGettag"); + setIfAvailable(*rpz.options, rustrpz.zoneSizeHint, "zoneSizeHint"); + setIfAvailable(*rpz.options, rustrpz.tsig, "tsig"); + setIfAvailable(*rpz.options, rustrpz.refresh, "refresh"); + setIfAvailable(*rpz.options, rustrpz.maxReceivedMBytes, "maxReceivedMBytes"); + setIfAvailable(*rpz.options, rustrpz.localAddress, "localAddress"); + setIfAvailable(*rpz.options, rustrpz.axfrTimeout, "axfrTimeout"); + setIfAvailable(*rpz.options, rustrpz.dumpFile, "dumpFile"); + setIfAvailable(*rpz.options, rustrpz.seedFile, "seedFile"); + } + rec.rpzs.emplace_back(rustrpz); + } +} + +string cvt(pdns::ZoneMD::Config cfg) +{ + switch (cfg) { + case pdns::ZoneMD::Config::Ignore: + return "ignore"; + case pdns::ZoneMD::Config::Validate: + return "validate"; + case pdns::ZoneMD::Config::Require: + return "require"; + } +} + +void fromLuaConfigToZoneToCache(const map& ztcConfigs, pdns::rust::settings::rec::Recordcache& recordcache) +{ + for (const auto& [_, iter] : ztcConfigs) { + pdns::rust::settings::rec::ZoneToCache ztc; + ztc.zone = iter.d_zone; + ztc.method = iter.d_method; + for (const auto& src : iter.d_sources) { + ztc.sources.emplace_back(src); + } + ztc.timeout = iter.d_timeout; + if (!iter.d_tt.name.empty()) { + ztc.tsig.name = iter.d_tt.name.toString(); + ztc.tsig.algo = iter.d_tt.algo.toString(); + ztc.tsig.secret = iter.d_tt.secret; + } + ztc.refreshPeriod = iter.d_refreshPeriod; + ztc.retryOnErrorPeriod = iter.d_retryOnError; + ztc.maxReceivedMBytes = iter.d_maxReceivedBytes; + ztc.localAddress = iter.d_local.toStringWithPort(); + ztc.zonemd = cvt(iter.d_zonemd); + ztc.dnssec = cvt(iter.d_dnssec); + recordcache.zonetocaches.emplace_back(ztc); + } +} +string cvt(AdditionalMode mode) +{ + switch (mode) { + case AdditionalMode::Ignore: + return "Ignore"; + case AdditionalMode::CacheOnly: + return "CacheOnly"; + case AdditionalMode::CacheOnlyRequireAuth: + return "CacheOnlyRequireAuth"; + case AdditionalMode::ResolveImmediately: + return "ResolveImmediately"; + case AdditionalMode::ResolveDeferred: + return "ResolveDeferred"; + } +} + +void fromLuaConfigToAllowAddtionalQTypes(const std::map, AdditionalMode>>& allowAdditionalQTypes, pdns::rust::settings::rec::Recursor& rec) +{ + for (const auto& [qtype, data] : allowAdditionalQTypes) { + const auto& [qtypeset, mode] = data; + pdns::rust::settings::rec::AllowedAdditionalQType add; + add.qtype = qtype.toString(); + for (const auto& extra : qtypeset) { + add.targets.emplace_back(extra.toString()); + } + add.mode = cvt(mode); + rec.allowed_additional_qtypes.emplace_back(add); + } +} + +void fromLuaConfigToProxyMappings(const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Incoming& incoming) +{ + for (const auto& mapping : proxyMapping) { + pdns::rust::settings::rec::ProxyMapping pmap; + pmap.subnet = mapping.first.toString(); + pmap.address = mapping.second.address.toString(); + if (mapping.second.suffixMatchNode) { + for (const auto& domain : mapping.second.suffixMatchNode->d_tree.getNodes()) { + pmap.domains.emplace_back(domain.toString()); + } + } + incoming.proxymappings.emplace_back(pmap); + } +} + +void fromLuaConfigToSortList(const SortList& arg, pdns::rust::settings::rec::Recursor& rec) +{ + const auto& sortlist = arg.getTree(); + for (const auto& iter : sortlist) { + pdns::rust::settings::rec::SortList rsl; + rsl.key = iter.first.toString(); + const auto& sub = iter.second; + // Some extra work to present them ordered in the YAML + std::set indexes; + std::multimap ordered; + for (auto& order : sub.d_orders) { + indexes.emplace(order.second); + ordered.emplace(order.second, order.first); + } + for (const auto& index : indexes) { + const auto& range = ordered.equal_range(index); + for (auto subnet = range.first; subnet != range.second; ++subnet) { + pdns::rust::settings::rec::SubnetOrder snorder; + snorder.order = index; + snorder.subnet = subnet->second.toString(); + rsl.subnets.emplace_back(snorder); + } + } + rec.sortlists.emplace_back(rsl); + } +} +} // namespace + +void pdns::settings::rec::fromLuaConfigToBridgeStruct(LocalStateHolder& luaConfig, const ProxyMapping& proxyMapping, pdns::rust::settings::rec::Recursorsettings& settings) +{ + + fromLuaConfigToTAInfo(luaConfig, settings.dnssec); + if (luaConfig->protobufExportConfig.enabled) { + pdns::rust::settings::rec::ProtobufServer pbServer; + fromLuaToProtobufServerStruct(luaConfig->protobufExportConfig, pbServer); + settings.logging.protobuf_servers.emplace_back(pbServer); + } + if (luaConfig->outgoingProtobufExportConfig.enabled) { + pdns::rust::settings::rec::ProtobufServer pbServer; + fromLuaToProtobufServerStruct(luaConfig->outgoingProtobufExportConfig, pbServer); + settings.logging.outgoing_protobuf_servers.emplace_back(pbServer); + } + + fromLuaConfigToRPZ(luaConfig->rpzRaw, settings.recursor); + fromLuaConfigToSortList(luaConfig->sortlist, settings.recursor); + fromLuaConfigToZoneToCache(luaConfig->ztcConfigs, settings.recordcache); + fromLuaConfigToAllowAddtionalQTypes(luaConfig->allowAdditionalQTypes, settings.recursor); + fromLuaConfigToProxyMappings(proxyMapping, settings.incoming); +} diff --git a/pdns/recursordist/settings/generate.py b/pdns/recursordist/settings/generate.py index b1cbb64b26..8515ec2a02 100644 --- a/pdns/recursordist/settings/generate.py +++ b/pdns/recursordist/settings/generate.py @@ -94,18 +94,29 @@ class LType(Enum): Bool = auto() Command = auto() Double = auto() + ListAllowedAdditionalQTypes = auto() ListAuthZones = auto() + ListDNSTapFrameStreamServers = auto() + ListDNSTapNODFrameStreamServers = auto() ListForwardZones = auto() ListNegativeTrustAnchors = auto() + ListProtobufServers = auto() + ListProxyMappings = auto() + ListRPZs = auto(); ListSocketAddresses = auto() + ListSortLists = auto() ListStrings = auto() ListSubnets = auto() ListTrustAnchors = auto() + ListZoneToCaches = auto() String = auto() Uint64 = auto() listOfStringTypes = (LType.ListSocketAddresses, LType.ListStrings, LType.ListSubnets) -listOfStructuredTypes = (LType.ListAuthZones, LType.ListForwardZones, LType.ListTrustAnchors, LType.ListNegativeTrustAnchors) +listOfStructuredTypes = (LType.ListAuthZones, LType.ListForwardZones, LType.ListTrustAnchors, LType.ListNegativeTrustAnchors, + LType.ListProtobufServers, LType.ListDNSTapFrameStreamServers, LType.ListDNSTapNODFrameStreamServers, + LType.ListSortLists, LType.ListRPZs, LType.ListZoneToCaches, LType.ListAllowedAdditionalQTypes, + LType.ListProxyMappings) def get_olddoc_typename(typ): """Given a type from table.py, return the old-style type name""" diff --git a/pdns/recursordist/settings/rust-bridge-in.rs b/pdns/recursordist/settings/rust-bridge-in.rs index b0c0bd2e64..f18c0a4aca 100644 --- a/pdns/recursordist/settings/rust-bridge-in.rs +++ b/pdns/recursordist/settings/rust-bridge-in.rs @@ -47,10 +47,211 @@ pub struct NegativeTrustAnchor { reason: String, } -// A struct holding bot a vector of forward zones and a vector o auth zones, used by REST API code +// A protobuf logging server +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct ProtobufServer { + #[serde(default, skip_serializing_if = "crate::is_default")] + servers: Vec, + #[serde(default = "crate::U64::<2>::value", skip_serializing_if = "crate::U64::<2>::is_equal")] + timeout: u64, + #[serde(default = "crate::U64::<100>::value", skip_serializing_if = "crate::U64::<100>::is_equal")] + maxQueuedEntries: u64, + #[serde(default = "crate::U64::<1>::value", skip_serializing_if = "crate::U64::<1>::is_equal")] + reconnectWaitTime: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + taggedOnly: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + asyncConnect: bool, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + logQueries: bool, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + logResponses: bool, + #[serde(default = "crate::def_pb_export_qtypes", skip_serializing_if = "crate::default_value_equal_pb_export_qtypes")] + exportTypes: Vec, + #[serde(default, skip_serializing_if = "crate::is_default")] + logMappedFrom: bool, +} + +// A dnstap logging server +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct DNSTapFrameStreamServer { + #[serde(default, skip_serializing_if = "crate::is_default")] + servers: Vec, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + logQueries: bool, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + logResponses: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + bufferHint: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + flushTimeout: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + inputQueueSize: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + outputQueueSize: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + queueNotifyThreshold: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + reopenInterval: u64, +} + +// A dnstap logging NOD server +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct DNSTapNODFrameStreamServer { + #[serde(default, skip_serializing_if = "crate::is_default")] + servers: Vec, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + logNODs: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + logUDRs: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + bufferHint: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + flushTimeout: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + inputQueueSize: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + outputQueueSize: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + queueNotifyThreshold: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + reopenInterval: u64, +} + +#[derive(Default, Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct TSIGTriplet { + #[serde(default, skip_serializing_if = "crate::is_default")] + name: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + algo: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + secret: String, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct RPZ { + #[serde(default, skip_serializing_if = "crate::is_default")] + addresses: Vec, + #[serde(default, skip_serializing_if = "crate::is_default")] + name: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + defcontent: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + defpol: String, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + defpolOverrideLocalData: bool, + #[serde(default = "crate::U32::<{u32::MAX}>::value", skip_serializing_if = "crate::U32::<{u32::MAX}>::is_equal")] + defttl: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + extendedErrorCode: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + extendedErrorExtra: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + includeSOA: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + ignoreDuplicates: bool, + #[serde(default = "crate::U32::<{u32::MAX}>::value", skip_serializing_if = "crate::U32::<{u32::MAX}>::is_equal")] + maxTTL: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + policyName: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + tags: Vec, + #[serde(default = "crate::Bool::::value", skip_serializing_if = "crate::if_true")] + overridesGettag: bool, + #[serde(default, skip_serializing_if = "crate::is_default")] + zoneSizeHint: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + tsig: TSIGTriplet, + #[serde(default, skip_serializing_if = "crate::is_default")] + refresh: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + maxReceivedMBytes: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + localAddress: String, + #[serde(default = "crate::U32::<20>::value", skip_serializing_if = "crate::U32::<20>::is_equal")] + axfrTimeout: u32, + #[serde(default, skip_serializing_if = "crate::is_default")] + dumpFile: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + seedFile: String, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct ZoneToCache { + #[serde(default, skip_serializing_if = "crate::is_default")] + zone: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + method: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + sources: Vec, + #[serde(default = "crate::U64::<20>::value", skip_serializing_if = "crate::U64::<20>::is_equal")] + timeout: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + tsig: TSIGTriplet, + #[serde(default = "crate::U64::<86400>::value", skip_serializing_if = "crate::U64::<86400>::is_equal")] + refreshPeriod: u64, + #[serde(default = "crate::U64::<60>::value", skip_serializing_if = "crate::U64::<60>::is_equal")] + retryOnErrorPeriod: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + maxReceivedMBytes: u64, + #[serde(default, skip_serializing_if = "crate::is_default")] + localAddress: String, + #[serde(default = "crate::def_ztc_validate", skip_serializing_if = "crate::def_value_equals_ztc_validate")] + zonemd: String, + #[serde(default = "crate::def_ztc_validate", skip_serializing_if = "crate::def_value_equals_ztc_validate")] + dnssec: String, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct SubnetOrder { + #[serde(default, skip_serializing_if = "crate::is_default")] + subnet: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + order: u32, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct SortList { + #[serde(default, skip_serializing_if = "crate::is_default")] + key: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + subnets: Vec, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct AllowedAdditionalQType { + #[serde(default, skip_serializing_if = "crate::is_default")] + qtype: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + targets: Vec, + #[serde(default = "crate::def_additional_mode", skip_serializing_if = "crate::default_value_equals_additional_mode")] + mode: String, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct ProxyMapping { + #[serde(default, skip_serializing_if = "crate::is_default")] + subnet: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + address: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + domains: Vec, +} + +// A struct holding both a vector of forward zones and a vector o auth zones, used by REST API code #[derive(Deserialize, Serialize, Debug, PartialEq)] #[serde(deny_unknown_fields)] -struct ApiZones { +pub struct ApiZones { #[serde(default, skip_serializing_if = "crate::is_default")] auth_zones: Vec, #[serde(default, skip_serializing_if = "crate::is_default")] @@ -69,6 +270,14 @@ struct Value { vec_authzone_val: Vec, vec_trustanchor_val: Vec, vec_negativetrustanchor_val: Vec, + vec_protobufserver_val: Vec, + vec_dnstap_framestream_server_val: Vec, + vec_dnstap_nod_framestream_server_val: Vec, + vec_rpz_val: Vec, + vec_sortlist_val: Vec, + vec_zonetocache_val: Vec, + vec_allowedadditionalqtype_val: Vec, + vec_proxymapping_val: Vec, } struct OldStyle { @@ -111,7 +320,7 @@ extern "Rust" { // Validate the sections inside the main settings struct, sections themselves will valdiate their fields fn validate(self: &Recursorsettings) -> Result<()>; - // The validate function bewlo are "hand-crafted" as their structs afre mnot generated + // The validate function below are "hand-crafted" as their structs are not generated fn validate(self: &AuthZone, field: &str) -> Result<()>; fn validate(self: &ForwardZone, field: &str) -> Result<()>; fn validate(self: &TrustAnchor, field: &str) -> Result<()>; diff --git a/pdns/recursordist/settings/rust/src/bridge.rs b/pdns/recursordist/settings/rust/src/bridge.rs index 80cd21c98a..dfb5ed6922 100644 --- a/pdns/recursordist/settings/rust/src/bridge.rs +++ b/pdns/recursordist/settings/rust/src/bridge.rs @@ -59,7 +59,6 @@ impl Default for NegativeTrustAnchor { } } - impl Default for ApiZones { fn default() -> Self { let deserialized: ApiZones = serde_yaml::from_str("").unwrap(); @@ -186,6 +185,41 @@ pub fn forward_zones_to_yaml_string(vec: &Vec) -> Result Result<(), ValidationError> { validate_name(&(field.to_owned() + ".zone"), &self.zone)?; @@ -207,14 +241,8 @@ impl ForwardZone { } let mut map = serde_yaml::Mapping::new(); - map.insert( - serde_yaml::Value::String("zone".to_owned()), - serde_yaml::Value::String(self.zone.to_owned()), - ); - map.insert( - serde_yaml::Value::String("recurse".to_owned()), - serde_yaml::Value::Bool(self.recurse), - ); + inserts(&mut map, "zone", &self.zone); + insertb(&mut map, "recurse", self.recurse); map.insert( serde_yaml::Value::String("forwarders".to_owned()), serde_yaml::Value::Sequence(seq), @@ -235,14 +263,8 @@ impl AuthZone { fn to_yaml_map(&self) -> serde_yaml::Value { let mut map = serde_yaml::Mapping::new(); - map.insert( - serde_yaml::Value::String("zone".to_owned()), - serde_yaml::Value::String(self.zone.to_owned()), - ); - map.insert( - serde_yaml::Value::String("file".to_owned()), - serde_yaml::Value::String(self.file.to_owned()), - ); + inserts(&mut map, "zone", &self.zone); + inserts(&mut map, "file", &self.file); serde_yaml::Value::Mapping(map) } } @@ -258,14 +280,8 @@ impl TrustAnchor { seq.push(serde_yaml::Value::String(entry.to_owned())); } let mut map = serde_yaml::Mapping::new(); - map.insert( - serde_yaml::Value::String("name".to_owned()), - serde_yaml::Value::String(self.name.to_owned()), - ); - map.insert( - serde_yaml::Value::String("dsrecord".to_owned()), - serde_yaml::Value::Sequence(seq), - ); + inserts(&mut map, "name", &self.name); + insertseq(&mut map, "dsrecords", &seq); serde_yaml::Value::Mapping(map) } } @@ -277,14 +293,229 @@ impl NegativeTrustAnchor { fn to_yaml_map(&self) -> serde_yaml::Value { let mut map = serde_yaml::Mapping::new(); + inserts(&mut map, "name", &self.name); + inserts(&mut map, "reason", &self.reason); + serde_yaml::Value::Mapping(map) + } +} + +impl ProtobufServer { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.servers { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + let mut map = serde_yaml::Mapping::new(); + insertseq(&mut map, "servers", &seq); + insertu(&mut map, "timeout", self.timeout); + insertu(&mut map, "maxQueuedEntries", self.maxQueuedEntries); + insertu(&mut map, "reconnectWaitTime", self.reconnectWaitTime); + insertb(&mut map, "taggedOnly", self.taggedOnly); + insertb(&mut map, "asyncConnect", self.asyncConnect); + insertb(&mut map, "logQueries", self.logQueries); + insertb(&mut map, "logResponses", self.logResponses); + let mut seq2 = serde_yaml::Sequence::new(); + for entry in &self.exportTypes { + seq2.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "exportTypes", &seq2); + serde_yaml::Value::Mapping(map) + } +} + +impl DNSTapFrameStreamServer { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.servers { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + let mut map = serde_yaml::Mapping::new(); + insertseq(&mut map, "servers", &seq); + insertb(&mut map, "logQueries", self.logQueries); + insertb(&mut map, "logResponses", self.logResponses); + insertu(&mut map, "bufferHint", self.bufferHint); + insertu(&mut map, "flushTimeout", self.flushTimeout); + insertu(&mut map, "inputQueueSize", self.inputQueueSize); + insertu(&mut map, "outputQueueSize", self.outputQueueSize); + insertu(&mut map, "queueNotifyThreshold", self.queueNotifyThreshold); + insertu(&mut map, "reopenInterval", self.reopenInterval); + serde_yaml::Value::Mapping(map) + } +} + +impl DNSTapNODFrameStreamServer { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.servers { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + let mut map = serde_yaml::Mapping::new(); + insertseq(&mut map, "servers", &seq); + insertb(&mut map, "logNODs", self.logNODs); + insertb(&mut map, "logUDRs", self.logUDRs); + insertu(&mut map, "bufferHint", self.bufferHint); + insertu(&mut map, "flushTimeout", self.flushTimeout); + insertu(&mut map, "inputQueueSize", self.inputQueueSize); + insertu(&mut map, "outputQueueSize", self.outputQueueSize); + insertu(&mut map, "queueNotifyThreshold", self.queueNotifyThreshold); + insertu(&mut map, "reopenInterval", self.reopenInterval); + serde_yaml::Value::Mapping(map) + } +} + +impl SortList { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut map = serde_yaml::Mapping::new(); + inserts(&mut map, "key", &self.key); + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.subnets { + let mut submap = serde_yaml::Mapping::new(); + inserts(&mut submap, "subnet", &entry.subnet); + insertu32(&mut submap, "order", entry.order); + seq.push(serde_yaml::Value::Mapping(submap)); + } + insertseq(&mut map, "subnets", &seq); + serde_yaml::Value::Mapping(map) + } +} + +impl RPZ { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut map = serde_yaml::Mapping::new(); + let mut seq1 = serde_yaml::Sequence::new(); + for entry in &self.addresses { + seq1.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "addresses", &seq1); + inserts(&mut map, "name", &self.name); + inserts(&mut map, "defcontent", &self.defcontent); + inserts(&mut map, "defpol", &self.defpol); + insertb( + &mut map, + "defpolOverrideLocalData", + self.defpolOverrideLocalData, + ); + insertu32(&mut map, "defttl", self.defttl); + insertu32(&mut map, "extendedErrorCode", self.extendedErrorCode); + insertb(&mut map, "includeSOA", self.includeSOA); + insertb(&mut map, "ignoreDuplicates", self.ignoreDuplicates); + insertu32(&mut map, "maxTTL", self.maxTTL); + inserts(&mut map, "policyName", &self.policyName); + let mut seq2 = serde_yaml::Sequence::new(); + for entry in &self.tags { + seq2.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "tags", &seq2); + insertb(&mut map, "overridesGettag", self.overridesGettag); + insertu32(&mut map, "zoneSizeHint", self.zoneSizeHint); + + let mut tsigmap = serde_yaml::Mapping::new(); + inserts(&mut tsigmap, "name", &self.tsig.name); + inserts(&mut tsigmap, "algo", &self.tsig.algo); + inserts(&mut tsigmap, "secret", &self.tsig.secret); map.insert( - serde_yaml::Value::String("name".to_owned()), - serde_yaml::Value::String(self.name.to_owned()), + serde_yaml::Value::String("tsig".to_owned()), + serde_yaml::Value::Mapping(tsigmap), ); + + insertu32(&mut map, "refresh", self.refresh); + insertu32(&mut map, "maxReceivedMBytes", self.maxReceivedMBytes); + inserts(&mut map, "localAddress", &self.localAddress); + insertu32(&mut map, "axfrTimeout", self.axfrTimeout); + inserts(&mut map, "dumpFile", &self.dumpFile); + inserts(&mut map, "seedFile", &self.seedFile); + serde_yaml::Value::Mapping(map) + } +} + +impl ZoneToCache { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut map = serde_yaml::Mapping::new(); + inserts(&mut map, "zone", &self.zone); + inserts(&mut map, "method", &self.method); + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.sources { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "sources", &seq); + insertu(&mut map, "timeout", self.timeout); + + let mut tsigmap = serde_yaml::Mapping::new(); + inserts(&mut tsigmap, "name", &self.tsig.name); + inserts(&mut tsigmap, "algo", &self.tsig.algo); + inserts(&mut tsigmap, "secret", &self.tsig.secret); map.insert( - serde_yaml::Value::String("reason".to_owned()), - serde_yaml::Value::String(self.reason.to_owned()), + serde_yaml::Value::String("tsig".to_owned()), + serde_yaml::Value::Mapping(tsigmap), ); + + insertu(&mut map, "refreshPeriod", self.refreshPeriod); + insertu(&mut map, "retryOnErrorPeriod", self.retryOnErrorPeriod); + insertu(&mut map, "maxReceivedMBytes", self.maxReceivedMBytes); + inserts(&mut map, "localAddress", &self.localAddress); + inserts(&mut map, "zonemd", &self.zonemd); + inserts(&mut map, "dnssec", &self.dnssec); + + serde_yaml::Value::Mapping(map) + } +} + +impl AllowedAdditionalQType { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut map = serde_yaml::Mapping::new(); + inserts(&mut map, "qtype", &self.qtype); + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.targets { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "targets", &seq); + inserts(&mut map, "mode", &self.mode); + serde_yaml::Value::Mapping(map) + } +} + +impl ProxyMapping { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + let mut map = serde_yaml::Mapping::new(); + inserts(&mut map, "subnet", &self.subnet); + inserts(&mut map, "address", &self.address); + let mut seq = serde_yaml::Sequence::new(); + for entry in &self.domains { + seq.push(serde_yaml::Value::String(entry.to_owned())); + } + insertseq(&mut map, "domains", &seq); serde_yaml::Value::Mapping(map) } } @@ -300,10 +531,7 @@ pub fn validate_forward_zones(field: &str, vec: &Vec) -> Result<(), } #[allow(clippy::ptr_arg)] //# Avoids creating a rust::Slice object on the C++ side. -pub fn validate_trustanchors( - field: &str, - vec: &Vec, -) -> Result<(), ValidationError> { +pub fn validate_trustanchors(field: &str, vec: &Vec) -> Result<(), ValidationError> { validate_vec(field, vec, |field, element| element.validate(field)) } @@ -457,7 +685,65 @@ pub fn map_to_yaml_string(vec: &Vec) -> Result serde_yaml::Value::String("map_to_yaml_string: Unknown type: ".to_owned() + other), + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_protobufserver_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_dnstap_framestream_server_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_dnstap_nod_framestream_server_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_rpz_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_sortlist_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_zonetocache_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_allowedadditionalqtype_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + "Vec" => { + let mut seq = serde_yaml::Sequence::new(); + for element in &entry.value.vec_proxymapping_val { + seq.push(element.to_yaml_map()); + } + serde_yaml::Value::Sequence(seq) + } + other => serde_yaml::Value::String( + "map_to_yaml_string: Unknown type: ".to_owned() + other, + ), }; if entry.overriding { let tagged_value = Box::new(serde_yaml::value::TaggedValue { @@ -592,3 +878,31 @@ pub fn api_delete_zone(path: &str, zone: &str) -> Result<(), std::io::Error> { zones.forward_zones.retain(|x| x.zone != zone); api_write_zones(path, &zones) } + +pub fn def_pb_export_qtypes() -> Vec { + vec![ + String::from("A"), + String::from("CNAME"), + String::from("AAAA"), + ] +} + +pub fn default_value_equal_pb_export_qtypes(value: &Vec) -> bool { + &def_pb_export_qtypes() == value +} + +pub fn def_ztc_validate() -> String { + String::from("validate") +} + +pub fn def_value_equals_ztc_validate(value: &String) -> bool { + &def_ztc_validate() == value +} + +pub fn def_additional_mode() -> String { + String::from("CacheOnlyRequireAuth") +} + +pub fn default_value_equals_additional_mode(value: &String) -> bool { + &def_additional_mode() == value +} diff --git a/pdns/recursordist/settings/rust/src/helpers.rs b/pdns/recursordist/settings/rust/src/helpers.rs index a49786f616..8bb99a2e32 100644 --- a/pdns/recursordist/settings/rust/src/helpers.rs +++ b/pdns/recursordist/settings/rust/src/helpers.rs @@ -44,6 +44,16 @@ impl U64 { } } +pub struct U32; +impl U32 { + pub const fn value() -> u32 { + U + } + pub fn is_equal(v: &u32) -> bool { + v == &U + } +} + // A helper to define constant value as a Rust path */ pub struct Bool; impl Bool { diff --git a/pdns/recursordist/settings/table.py b/pdns/recursordist/settings/table.py index 64c6cbfa29..5e91bd3770 100644 --- a/pdns/recursordist/settings/table.py +++ b/pdns/recursordist/settings/table.py @@ -3232,6 +3232,114 @@ XXX 'default' : '24', 'help' : 'XXX', 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'protobuf_servers', + 'section' : 'logging', + 'type' : LType.ListProtobufServers, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'outgoing_protobuf_servers', + 'section' : 'logging', + 'type' : LType.ListProtobufServers, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'dnstap_framestream_servers', + 'section' : 'logging', + 'type' : LType.ListDNSTapFrameStreamServers, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'dnstap_nod_framestream_servers', + 'section' : 'logging', + 'type' : LType.ListDNSTapNODFrameStreamServers, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'sortlists', + 'section' : 'recursor', + 'type' : LType.ListSortLists, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'rpzs', + 'section' : 'recursor', + 'type' : LType.ListRPZs, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'zonetocaches', + 'section' : 'recordcache', + 'type' : LType.ListZoneToCaches, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'allowed_additional_qtypes', + 'section' : 'recursor', + 'type' : LType.ListAllowedAdditionalQTypes, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'proxymappings', + 'section' : 'incoming', + 'type' : LType.ListProxyMappings, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', XXX ''', 'skip-old' : True, diff --git a/pdns/recursordist/test-settings.cc b/pdns/recursordist/test-settings.cc index 0e1eb5b002..7335c6021e 100644 --- a/pdns/recursordist/test-settings.cc +++ b/pdns/recursordist/test-settings.cc @@ -422,4 +422,390 @@ BOOST_AUTO_TEST_CASE(test_rust_merge_override) BOOST_CHECK_EQUAL(lhs.recordcache.max_entries, 99U); } +BOOST_AUTO_TEST_CASE(test_yaml_defaults_ta) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(dnssec: + trustanchors: + - name: a + dsrecords: [b] + negative_trustanchors: + - name: c + reason: d + trustanchorfile: e + trustanchorfile_interval: 99 +)EOT"; + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(std::string(settings.dnssec.trustanchors[0].name), "a"); + BOOST_CHECK_EQUAL(std::string(settings.dnssec.trustanchors[0].dsrecords[0]), "b"); + BOOST_CHECK_EQUAL(std::string(settings.dnssec.negative_trustanchors[0].name), "c"); + BOOST_CHECK_EQUAL(std::string(settings.dnssec.negative_trustanchors[0].reason), "d"); + BOOST_CHECK_EQUAL(std::string(settings.dnssec.trustanchorfile), "e"); + BOOST_CHECK_EQUAL(settings.dnssec.trustanchorfile_interval, 99U); +} + +BOOST_AUTO_TEST_CASE(test_yaml_defaults_protobuf) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(logging: + protobuf_servers: + - servers: [a] + - servers: [b] + timeout: 100 + maxQueuedEntries: 101 + reconnectWaitTime: 102 + taggedOnly: true + asyncConnect: true + logQueries: false + logResponses: false + logMappedFrom: true +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].timeout, 2U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].maxQueuedEntries, 100U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].reconnectWaitTime, 1U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].taggedOnly, false); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].asyncConnect, false); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].logQueries, true); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].logResponses, true); + // Code below crashes clang + // std::vector testv = {"A", "AAAA", "CNAME"}) + // BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].exportTypes, testv); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[0].logMappedFrom, false); + + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].timeout, 100U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].maxQueuedEntries, 101U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].reconnectWaitTime, 102U); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].taggedOnly, true); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].asyncConnect, true); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].logQueries, false); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].logResponses, false); + BOOST_CHECK_EQUAL(settings.logging.protobuf_servers[1].logMappedFrom, true); +} + +BOOST_AUTO_TEST_CASE(test_yaml_defaults_outgoing_protobuf) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(logging: + outgoing_protobuf_servers: + - servers: [a] + - servers: [b] + timeout: 100 + maxQueuedEntries: 101 + reconnectWaitTime: 102 + taggedOnly: true + asyncConnect: true + logQueries: false + logResponses: false + logMappedFrom: true +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].timeout, 2U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].maxQueuedEntries, 100U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].reconnectWaitTime, 1U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].taggedOnly, false); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].asyncConnect, false); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].logQueries, true); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].logResponses, true); + // Code below crashes clang + // std::vector testv = {"A", "AAAA", "CNAME"}) + // BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].exportTypes, testv); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[0].logMappedFrom, false); + + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].timeout, 100U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].maxQueuedEntries, 101U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].reconnectWaitTime, 102U); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].taggedOnly, true); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].asyncConnect, true); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].logQueries, false); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].logResponses, false); + BOOST_CHECK_EQUAL(settings.logging.outgoing_protobuf_servers[1].logMappedFrom, true); +} + +BOOST_AUTO_TEST_CASE(test_yaml_defaults_dnstap) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(logging: + dnstap_framestream_servers: + - servers: [a] + - servers: [b] + logQueries: false + logResponses: false + bufferHint: 1 + flushTimeout: 2 + inputQueueSize: 3 + outputQueueSize: 4 + queueNotifyThreshold: 5 + reopenInterval: 6 +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].logQueries, true); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].logResponses, true); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].bufferHint, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].flushTimeout, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].inputQueueSize, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].outputQueueSize, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].queueNotifyThreshold, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[0].reopenInterval, 0U); + + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].logQueries, false); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].logResponses, false); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].bufferHint, 1U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].flushTimeout, 2U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].inputQueueSize, 3U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].outputQueueSize, 4U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].queueNotifyThreshold, 5U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_framestream_servers[1].reopenInterval, 6U); +} + +BOOST_AUTO_TEST_CASE(test_yaml_defaults_dnstapnod) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(logging: + dnstap_nod_framestream_servers: + - servers: [a] + - servers: [b] + logNODs: false + logUDRs: true + bufferHint: 1 + flushTimeout: 2 + inputQueueSize: 3 + outputQueueSize: 4 + queueNotifyThreshold: 5 + reopenInterval: 6 +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].logNODs, true); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].logUDRs, false); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].bufferHint, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].flushTimeout, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].inputQueueSize, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].outputQueueSize, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].queueNotifyThreshold, 0U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[0].reopenInterval, 0U); + + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].logNODs, false); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].logUDRs, true); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].bufferHint, 1U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].flushTimeout, 2U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].inputQueueSize, 3U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].outputQueueSize, 4U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].queueNotifyThreshold, 5U); + BOOST_CHECK_EQUAL(settings.logging.dnstap_nod_framestream_servers[1].reopenInterval, 6U); +} + +BOOST_AUTO_TEST_CASE(test_yaml_defaults_rpz) +{ + // Two entries: one all default, one all overrides + const std::string yaml = R"EOT(recursor: + rpzs: + - name: file + - name: zone + addresses: [1.2.3.4] + - name: nondef + addresses: [1.2.3.4] + defcontent: a + defpol: b + defpolOverrideLocalData: false + defttl: 99 + extendedErrorCode: 100 + extendedErrorExtra: c + includeSOA: true + ignoreDuplicates: true + maxTTL: 101 + policyName: c + tags: [d,e] + overridesGettag: false + zoneSizeHint: 102 + tsig: + name: f + algo: g + secret: h + refresh: 103 + maxReceivedMBytes: 104 + localAddress: i + axfrTimeout: 105 + dumpFile: j + seedFile: k +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[0].name), "file"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[0].defcontent), ""); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[0].defpol), ""); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].defpolOverrideLocalData, true); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].defttl, -1U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].extendedErrorCode, 0U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].extendedErrorExtra, ""); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].includeSOA, false); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].ignoreDuplicates, false); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].maxTTL, -1U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].tags.size(), 0U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].overridesGettag, true); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[0].zoneSizeHint, 0U); + + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].name), "zone"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].addresses[0]), "1.2.3.4"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].tsig.name), ""); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].tsig.algo), ""); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].tsig.secret), ""); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[1].refresh, 0U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[1].maxReceivedMBytes, 0U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].localAddress), ""); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[1].axfrTimeout, 20U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].dumpFile), ""); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].seedFile), ""); + + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].name), "nondef"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[1].addresses[0]), "1.2.3.4"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].defcontent), "a"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].defpol), "b"); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].defpolOverrideLocalData, false); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].defttl, 99U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].extendedErrorCode, 100U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].extendedErrorExtra, "c"); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].includeSOA, true); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].ignoreDuplicates, true); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].maxTTL, 101U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].tags.size(), 2U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].overridesGettag, false); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].zoneSizeHint, 102U); + + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].tsig.name), "f"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].tsig.algo), "g"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].tsig.secret), "h"); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].refresh, 103U); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].maxReceivedMBytes, 104U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].localAddress), "i"); + BOOST_CHECK_EQUAL(settings.recursor.rpzs[2].axfrTimeout, 105U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].dumpFile), "j"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.rpzs[2].seedFile), "k"); +} + +BOOST_AUTO_TEST_CASE(test_yaml_sortlist) +{ + const std::string yaml = R"EOT(recursor: + sortlists: + - key: 1.2.3.4/8 + subnets: + - subnet: 5.6.7.8 + order: 99 +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(std::string(settings.recursor.sortlists[0].key), "1.2.3.4/8"); + BOOST_CHECK_EQUAL(std::string(settings.recursor.sortlists[0].subnets[0].subnet), "5.6.7.8"); + BOOST_CHECK_EQUAL(settings.recursor.sortlists[0].subnets[0].order, 99U); +} + +BOOST_AUTO_TEST_CASE(test_yaml_ztc) +{ + const std::string yaml = R"EOT(recordcache: + zonetocaches: + - zone: zone + method: axfr + sources: [1.2.3.4] + - zone: zone2 + method: axfr + sources: [1.2.3.4] + timeout: 1 + tsig: + name: a + algo: b + secret: c + refreshPeriod: 2 + retryOnErrorPeriod: 3 + maxReceivedMBytes: 4 + localAddress: d + zonemd: ignore + dnssec: require +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].zone), "zone"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].method), "axfr"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].sources[0]), "1.2.3.4"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].timeout, 20U); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].tsig.name), ""); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].tsig.algo), ""); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[0].tsig.secret), ""); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].refreshPeriod, 86400U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].retryOnErrorPeriod, 60U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].maxReceivedMBytes, 0U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].localAddress, ""); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].zonemd, "validate"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[0].dnssec, "validate"); + + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].zone), "zone2"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].method), "axfr"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].sources[0]), "1.2.3.4"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].timeout, 1U); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].tsig.name), "a"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].tsig.algo), "b"); + BOOST_CHECK_EQUAL(std::string(settings.recordcache.zonetocaches[1].tsig.secret), "c"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].refreshPeriod, 2U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].retryOnErrorPeriod, 3U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].maxReceivedMBytes, 4U); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].localAddress, "d"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].zonemd, "ignore"); + BOOST_CHECK_EQUAL(settings.recordcache.zonetocaches[1].dnssec, "require"); +} + +BOOST_AUTO_TEST_CASE(test_yaml_additionals) +{ + const std::string yaml = R"EOT(recursor: + allowed_additional_qtypes: + - qtype: A + targets: [A, MX, AAAA] + - qtype: MX + targets: [A] + mode: CacheOnly +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(std::string(settings.recursor.allowed_additional_qtypes[0].qtype), "A"); + BOOST_CHECK_EQUAL(settings.recursor.allowed_additional_qtypes[0].targets.size(), 3U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.allowed_additional_qtypes[0].mode), "CacheOnlyRequireAuth"); + + BOOST_CHECK_EQUAL(std::string(settings.recursor.allowed_additional_qtypes[1].qtype), "MX"); + BOOST_CHECK_EQUAL(settings.recursor.allowed_additional_qtypes[1].targets.size(), 1U); + BOOST_CHECK_EQUAL(std::string(settings.recursor.allowed_additional_qtypes[1].mode), "CacheOnly"); +} + +BOOST_AUTO_TEST_CASE(test_yaml_proxymapping) +{ + const std::string yaml = R"EOT(incoming: + proxymappings: + - subnet: 1.2.3.4 + address: 4.5.6.7 + - subnet: 3.4.5.6 + address: 6.7.8.9 + domains: [a, b, c] +)EOT"; + + auto settings = pdns::rust::settings::rec::parse_yaml_string(yaml); + settings.validate(); + BOOST_CHECK_EQUAL(std::string(settings.incoming.proxymappings[0].subnet), "1.2.3.4"); + BOOST_CHECK_EQUAL(std::string(settings.incoming.proxymappings[0].address), "4.5.6.7"); + BOOST_CHECK_EQUAL(settings.incoming.proxymappings[0].domains.size(), 0U); + + BOOST_CHECK_EQUAL(std::string(settings.incoming.proxymappings[1].subnet), "3.4.5.6"); + BOOST_CHECK_EQUAL(std::string(settings.incoming.proxymappings[1].address), "6.7.8.9"); + BOOST_CHECK_EQUAL(settings.incoming.proxymappings[1].domains.size(), 3U); +} + BOOST_AUTO_TEST_SUITE_END()