From: Otto Moerbeek Date: Wed, 6 Dec 2023 14:01:53 +0000 (+0100) Subject: Basic handling of YAML TAs and NTAs X-Git-Tag: rec-5.1.0-alpha1~9^2~19 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3816999818d215f6b8c3e76a9cc9ff3bb03d2758;p=thirdparty%2Fpdns.git Basic handling of YAML TAs and NTAs Current code can convert Lua into YAML Nothing is done yet with YAML if found --- diff --git a/pdns/recursordist/rec-main.cc b/pdns/recursordist/rec-main.cc index aded778cf5..b441f3acc9 100644 --- a/pdns/recursordist/rec-main.cc +++ b/pdns/recursordist/rec-main.cc @@ -2894,7 +2894,7 @@ static void recursorThread() } } -static pair doYamlConfig(Logr::log_t /* startupLog */, int argc, char* argv[]) // NOLINT: Posix API +static pair doYamlConfig(Logr::log_t startupLog, int argc, char* argv[]) // NOLINT: Posix API { if (!::arg().mustDo("config")) { return {0, false}; @@ -2904,6 +2904,31 @@ static pair doYamlConfig(Logr::log_t /* startupLog */, int argc, char ::arg().parse(argc, argv); pdns::rust::settings::rec::Recursorsettings settings; pdns::settings::rec::oldStyleSettingsToBridgeStruct(settings); + luaConfigDelayedThreads delayedLuaThreads; + try { + ProxyMapping proxyMapping; + loadRecursorLuaConfig(::arg()["lua-config-file"], delayedLuaThreads, proxyMapping); + } + catch (PDNSException& e) { + SLOG(g_log << Logger::Error << "Cannot load Lua configuration: " << e.reason << endl, + 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); + } + auto yaml = settings.to_yaml_string(); cout << yaml << endl; } diff --git a/pdns/recursordist/settings/cxxsupport.cc b/pdns/recursordist/settings/cxxsupport.cc index 81e064ab79..9c690a0bff 100644 --- a/pdns/recursordist/settings/cxxsupport.cc +++ b/pdns/recursordist/settings/cxxsupport.cc @@ -518,7 +518,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,7 +618,7 @@ 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)) { diff --git a/pdns/recursordist/settings/generate.py b/pdns/recursordist/settings/generate.py index e60dea3272..b1cbb64b26 100644 --- a/pdns/recursordist/settings/generate.py +++ b/pdns/recursordist/settings/generate.py @@ -39,12 +39,13 @@ # default: default value, use 'true' and 'false' for Booleans # help: short help text # doc: the docs text will be put here -# doc-rst: optional .rst annotations, typically used for ..version_added/changed:: +# doc-rst: optional .rst annotations # doc-new: optional docs text specific to YAML format, e.g. not talking about comma separated # lists and giving YAML examples. (optional) -# skip-yamL: optional if this key is present, the field wil be skipped in the new style settings. +# skip-yaml: optional if this key is present, the field will be skipped in the new style settings. # Use for deprecated settings, the generated code will use deprecated map from arg() # to handle old names when converting .conf to .yml +# skip-old: optional if this key is present, the field will be skipped in the old style settings. # versionadded: string or list of strings # # The above struct will generate in cxxsettings-generated.cc: @@ -94,13 +95,18 @@ class LType(Enum): Command = auto() Double = auto() ListAuthZones = auto() + ListForwardZones = auto() + ListNegativeTrustAnchors = auto() ListSocketAddresses = auto() ListStrings = auto() ListSubnets = auto() - ListForwardZones = auto() + ListTrustAnchors = auto() String = auto() Uint64 = auto() +listOfStringTypes = (LType.ListSocketAddresses, LType.ListStrings, LType.ListSubnets) +listOfStructuredTypes = (LType.ListAuthZones, LType.ListForwardZones, LType.ListTrustAnchors, LType.ListNegativeTrustAnchors) + def get_olddoc_typename(typ): """Given a type from table.py, return the old-style type name""" if typ == LType.Bool: @@ -143,6 +149,10 @@ def get_newdoc_typename(typ): return 'Sequence of `Forward Zone`_' if typ == LType.ListAuthZones: return 'Sequence of `Auth Zone`_' + if typ == LType.ListTrustAnchors: + return 'Sequence of `TrustAnchors`_' + if typ == LType.ListNegativeTrustAnchors: + return 'Sequence of `NegativeTrustAnchors`_' return 'Unknown' + str(typ) def get_default_olddoc_value(typ, val): @@ -179,6 +189,13 @@ def get_default_newdoc_value(typ, val): ret = '' return '``[' + ret + ']``' +def list_to_base_type(typ): + typeName = typ.name + if typeName.startswith('List') and typeName.endswith('s'): + baseName = typeName[4:len(typeName) - 1] + return baseName + return 'Unknown: ' + typeName + def get_rust_type(typ): """Determine which Rust type is used for a logical type""" if typ == LType.Bool: @@ -189,17 +206,15 @@ def get_rust_type(typ): return 'f64' if typ == LType.String: return 'String' + # These vectors map to Vec if typ == LType.ListSocketAddresses: return 'Vec' if typ == LType.ListSubnets: return 'Vec' if typ == LType.ListStrings: return 'Vec' - if typ == LType.ListForwardZones: - return 'Vec' - if typ == LType.ListAuthZones: - return 'Vec' - return 'Unknown' + str(typ) + # These vectors map to Vec + return 'Vec<' + list_to_base_type(typ) + '>' def quote(arg): """Return a quoted string""" @@ -237,6 +252,8 @@ def gen_cxx_oldstylesettingstobridgestruct(file, entries): continue if 'skip-yaml' in entry: continue + if 'skip-old' in entry: + continue rust_type = get_rust_type(entry['type']) name = entry['name'] oldname = entry['oldname'] @@ -276,6 +293,8 @@ def gen_cxx_oldkvtobridgestruct(file, entries): continue if 'skip-yaml' in entry: continue + if 'skip-old' in entry: + continue rust_type = get_rust_type(entry['type']) extra = '' if entry['oldname'] == 'forward-zones-recurse': @@ -322,6 +341,8 @@ def gen_cxx_brigestructtoldstylesettings(file, entries): continue if 'skip-yaml' in entry: continue + if 'skip-old' in entry: + continue section = entry['section'].lower() name = entry['name'] oldname = entry['oldname'] @@ -353,26 +374,13 @@ def is_value_rust_default(typ, value): return value == '' return False -def gen_rust_forwardzonevec_default_functions(name): - """Generate Rust code for the default handling of a vector for ForwardZones""" - ret = f'// DEFAULT HANDLING for {name}\n' - ret += f'fn default_value_{name}() -> Vec {{\n' - ret += ' Vec::new()\n' - ret += '}\n' - ret += f'fn default_value_equal_{name}(value: &Vec)' - ret += '-> bool {\n' - ret += f' let def = default_value_{name}();\n' - ret += ' &def == value\n' - ret += '}\n\n' - return ret - -def gen_rust_authzonevec_default_functions(name): - """Generate Rust code for the default handling of a vector for AuthZones""" +def gen_rust_vec_default_functions(name, typeName): + """Generate Rust code for the default handling of a vector for typeName""" ret = f'// DEFAULT HANDLING for {name}\n' - ret += f'fn default_value_{name}() -> Vec {{\n' + ret += f'fn default_value_{name}() -> Vec {{\n' ret += ' Vec::new()\n' ret += '}\n' - ret += f'fn default_value_equal_{name}(value: &Vec)' + ret += f'fn default_value_equal_{name}(value: &Vec)' ret += '-> bool {\n' ret += f' let def = default_value_{name}();\n' ret += ' &def == value\n' @@ -394,7 +402,7 @@ def gen_rust_stringvec_default_functions(entry, name): parts = re.split('[ \t,]+', entry['default']) if len(parts) > 0: ret += ' vec![\n' - for part in parts: + for part in parts: if part == '': continue ret += f' String::from({quote(part)}),\n' @@ -410,12 +418,11 @@ def gen_rust_stringvec_default_functions(entry, name): def gen_rust_default_functions(entry, name, rust_type): """Generate Rust code for the default handling""" - if entry['type'] in (LType.ListSocketAddresses, LType.ListSubnets, LType.ListStrings): + if entry['type'] in listOfStringTypes: return gen_rust_stringvec_default_functions(entry, name) - if entry['type'] == LType.ListForwardZones: - return gen_rust_forwardzonevec_default_functions(name) - if entry['type'] == LType.ListAuthZones: - return gen_rust_authzonevec_default_functions(name) + if entry['type'] in listOfStructuredTypes: + baseName = list_to_base_type(entry['type']) + return gen_rust_vec_default_functions(name, baseName) ret = f'// DEFAULT HANDLING for {name}\n' ret += f'fn default_value_{name}() -> {rust_type} {{\n' defvalue = entry['default'] @@ -501,9 +508,7 @@ def write_validator(file, section, entries): validator = 'validate_subnet' elif typ == LType.ListSocketAddresses: validator = 'validate_socket_address' - elif typ == LType.ListForwardZones: - validator = '|field, element| element.validate(field)' - elif typ == LType.ListAuthZones: + elif typ in listOfStructuredTypes: validator = '|field, element| element.validate(field)' else: continue @@ -638,6 +643,8 @@ def gen_oldstyle_docs(entries): continue if entry['doc'].strip() == 'SKIP': continue + if 'skip-old' in entry: + continue oldname = entry['oldname'] section = entry['section'] file.write(f'.. _setting-{oldname}:\n\n') diff --git a/pdns/recursordist/settings/rust-bridge-in.rs b/pdns/recursordist/settings/rust-bridge-in.rs index 40e53b144f..b0c0bd2e64 100644 --- a/pdns/recursordist/settings/rust-bridge-in.rs +++ b/pdns/recursordist/settings/rust-bridge-in.rs @@ -27,6 +27,26 @@ pub struct AuthZone { file: String, } +// A single trust anchor +#[derive(Deserialize, Serialize, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct TrustAnchor { + #[serde(default, skip_serializing_if = "crate::is_default")] + name: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + dsrecords: Vec, +} + +// A single negative trust anchor +#[derive(Deserialize, Serialize, Debug, PartialEq)] +#[serde(deny_unknown_fields)] +pub struct NegativeTrustAnchor { + #[serde(default, skip_serializing_if = "crate::is_default")] + name: String, + #[serde(default, skip_serializing_if = "crate::is_default")] + reason: String, +} + // A struct holding bot 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)] @@ -47,6 +67,8 @@ struct Value { vec_string_val: Vec, vec_forwardzone_val: Vec, vec_authzone_val: Vec, + vec_trustanchor_val: Vec, + vec_negativetrustanchor_val: Vec, } struct OldStyle { @@ -92,6 +114,8 @@ extern "Rust" { // The validate function bewlo are "hand-crafted" as their structs afre mnot generated fn validate(self: &AuthZone, field: &str) -> Result<()>; fn validate(self: &ForwardZone, field: &str) -> Result<()>; + fn validate(self: &TrustAnchor, field: &str) -> Result<()>; + fn validate(self: &NegativeTrustAnchor, field: &str) -> Result<()>; fn validate(self: &ApiZones, field: &str) -> Result<()>; // Helper functions to call the proper validate function on vectors of various kinds @@ -106,5 +130,7 @@ extern "Rust" { fn api_add_auth_zone(file: &str, authzone: AuthZone) -> Result<()>; fn api_add_forward_zone(file: &str, forwardzone: ForwardZone) -> Result<()>; fn api_add_forward_zones(file: &str, forwardzones: &mut Vec) -> Result<()>; + fn validate_trustanchors(field: &str, vec: &Vec) -> Result<()>; + fn validate_negativetrustanchors(field: &str, vec: &Vec) -> Result<()>; fn api_delete_zone(file: &str, zone: &str) -> Result<()>; } diff --git a/pdns/recursordist/settings/rust/src/bridge.rs b/pdns/recursordist/settings/rust/src/bridge.rs index 6de3318c2e..80cd21c98a 100644 --- a/pdns/recursordist/settings/rust/src/bridge.rs +++ b/pdns/recursordist/settings/rust/src/bridge.rs @@ -45,6 +45,21 @@ impl Default for AuthZone { } } +impl Default for TrustAnchor { + fn default() -> Self { + let deserialized: TrustAnchor = serde_yaml::from_str("").unwrap(); + deserialized + } +} + +impl Default for NegativeTrustAnchor { + fn default() -> Self { + let deserialized: NegativeTrustAnchor = serde_yaml::from_str("").unwrap(); + deserialized + } +} + + impl Default for ApiZones { fn default() -> Self { let deserialized: ApiZones = serde_yaml::from_str("").unwrap(); @@ -232,6 +247,48 @@ impl AuthZone { } } +impl TrustAnchor { + 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.dsrecords { + 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), + ); + serde_yaml::Value::Mapping(map) + } +} + +impl NegativeTrustAnchor { + pub fn validate(&self, _field: &str) -> Result<(), ValidationError> { + Ok(()) + } + + fn to_yaml_map(&self) -> serde_yaml::Value { + 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("reason".to_owned()), + serde_yaml::Value::String(self.reason.to_owned()), + ); + serde_yaml::Value::Mapping(map) + } +} + #[allow(clippy::ptr_arg)] //# Avoids creating a rust::Slice object on the C++ side. pub fn validate_auth_zones(field: &str, vec: &Vec) -> Result<(), ValidationError> { validate_vec(field, vec, |field, element| element.validate(field)) @@ -242,6 +299,22 @@ pub fn validate_forward_zones(field: &str, vec: &Vec) -> Result<(), validate_vec(field, vec, |field, element| element.validate(field)) } +#[allow(clippy::ptr_arg)] //# Avoids creating a rust::Slice object on the C++ side. +pub fn validate_trustanchors( + field: &str, + vec: &Vec, +) -> Result<(), ValidationError> { + validate_vec(field, vec, |field, element| element.validate(field)) +} + +#[allow(clippy::ptr_arg)] //# Avoids creating a rust::Slice object on the C++ side. +pub fn validate_negativetrustanchors( + field: &str, + vec: &Vec, +) -> Result<(), ValidationError> { + validate_vec(field, vec, |field, element| element.validate(field)) +} + pub fn allow_from_to_yaml_string(vec: &Vec) -> Result { let mut seq = serde_yaml::Sequence::new(); for entry in vec { @@ -370,9 +443,21 @@ 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_trustanchor_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_negativetrustanchor_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 { diff --git a/pdns/recursordist/settings/table.py b/pdns/recursordist/settings/table.py index da34538168..64c6cbfa29 100644 --- a/pdns/recursordist/settings/table.py +++ b/pdns/recursordist/settings/table.py @@ -3187,6 +3187,54 @@ If set to zero (the default), the value :ref:`setting-system-resolver-ttl` is us Warn on potential self-resolve. If this check draws the wrong conclusion, you can disable it. ''', - 'versionadded': '5.1.0' + 'versionadded': '5.1.0' + }, + { + 'name' : 'trustanchors', + 'section' : 'dnssec', + 'type' : LType.ListTrustAnchors, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'negative_trustanchors', + 'section' : 'dnssec', + 'type' : LType.ListNegativeTrustAnchors, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'trustanchorfile', + 'section' : 'dnssec', + 'type' : LType.String, + 'default' : '', + 'help' : 'XXX', + 'doc' : ''', + XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', + }, + { + 'name' : 'trustanchorfile_interval', + 'section' : 'dnssec', + 'type' : LType.Uint64, + 'default' : '24', + 'help' : 'XXX', + 'doc' : ''', +XXX + ''', + 'skip-old' : True, + 'versionadded': '5.1.0', }, ]