]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Basic handling of YAML TAs and NTAs
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 6 Dec 2023 14:01:53 +0000 (15:01 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Thu, 25 Apr 2024 09:31:40 +0000 (11:31 +0200)
Current code can convert Lua into YAML
Nothing is done yet with YAML if found

pdns/recursordist/rec-main.cc
pdns/recursordist/settings/cxxsupport.cc
pdns/recursordist/settings/generate.py
pdns/recursordist/settings/rust-bridge-in.rs
pdns/recursordist/settings/rust/src/bridge.rs
pdns/recursordist/settings/table.py

index aded778cf5b507a9fc5a512e97e9acff85c0d87f..b441f3acc9e635d272710d1eac9f563ae7eb7816 100644 (file)
@@ -2894,7 +2894,7 @@ static void recursorThread()
   }
 }
 
-static pair<int, bool> doYamlConfig(Logr::log_t /* startupLog */, int argc, char* argv[]) // NOLINT: Posix API
+static pair<int, bool> 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<int, bool> 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<rust::String> 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;
   }
index 81e064ab790354f89c4095815ebc0edd0570b363..9c690a0bff30462b57af08e435241988d5051aa6 100644 (file)
@@ -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)) {
index e60dea3272a8c7dacc5ead381d15b2416965efea..b1cbb64b262efc884167589d8405655c8cc4b132 100644 (file)
 #   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<String>
     if typ == LType.ListSocketAddresses:
         return 'Vec<String>'
     if typ == LType.ListSubnets:
         return 'Vec<String>'
     if typ == LType.ListStrings:
         return 'Vec<String>'
-    if typ == LType.ListForwardZones:
-        return 'Vec<ForwardZone>'
-    if typ == LType.ListAuthZones:
-        return 'Vec<AuthZone>'
-    return 'Unknown' + str(typ)
+    # These vectors map to Vec<Type>
+    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<recsettings::ForwardZone> {{\n'
-    ret += '    Vec::new()\n'
-    ret += '}\n'
-    ret += f'fn default_value_equal_{name}(value: &Vec<recsettings::ForwardZone>)'
-    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<recsettings::AuthZone> {{\n'
+    ret += f'fn default_value_{name}() -> Vec<recsettings::{typeName}> {{\n'
     ret += '    Vec::new()\n'
     ret += '}\n'
-    ret += f'fn default_value_equal_{name}(value: &Vec<recsettings::AuthZone>)'
+    ret += f'fn default_value_equal_{name}(value: &Vec<recsettings::{typeName}>)'
     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')
index 40e53b144f0ce06df43baeff3e6ea9df53d06acc..b0c0bd2e6430baf4b5ef5d0ccfede04396bd28ef 100644 (file)
@@ -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<String>,
+}
+
+// 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<String>,
     vec_forwardzone_val: Vec<ForwardZone>,
     vec_authzone_val: Vec<AuthZone>,
+    vec_trustanchor_val: Vec<TrustAnchor>,
+    vec_negativetrustanchor_val: Vec<NegativeTrustAnchor>,
 }
 
 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<ForwardZone>) -> Result<()>;
+    fn validate_trustanchors(field: &str, vec: &Vec<TrustAnchor>) -> Result<()>;
+    fn validate_negativetrustanchors(field: &str, vec: &Vec<NegativeTrustAnchor>) -> Result<()>;
     fn api_delete_zone(file: &str, zone: &str) -> Result<()>;
 }
index 6de3318c2e44bf43575b6d688d8d30e0a672af8c..80cd21c98a70860ae5a232a01a3f0284d62f0938 100644 (file)
@@ -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<AuthZone>) -> Result<(), ValidationError> {
     validate_vec(field, vec, |field, element| element.validate(field))
@@ -242,6 +299,22 @@ pub fn validate_forward_zones(field: &str, vec: &Vec<ForwardZone>) -> 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<TrustAnchor>,
+) -> 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<NegativeTrustAnchor>,
+) -> Result<(), ValidationError> {
+    validate_vec(field, vec, |field, element| element.validate(field))
+}
+
 pub fn allow_from_to_yaml_string(vec: &Vec<String>) -> Result<String, serde_yaml::Error> {
     let mut seq = serde_yaml::Sequence::new();
     for entry in vec {
@@ -370,9 +443,21 @@ pub fn map_to_yaml_string(vec: &Vec<OldStyle>) -> Result<String, serde_yaml::Err
                         }
                         serde_yaml::Value::Sequence(seq)
                     }
-                    other => serde_yaml::Value::String(
-                        "map_to_yaml_string: Unknown type: ".to_owned() + other,
-                    ),
+                    "Vec<TrustAnchor>" => {
+                        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<NegativeTrustAnchor>" => {
+                        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 {
index da34538168242f3c0b2492b041c215e9e47406b3..64c6cbfa29cc06e05ec51f43028920562e0a4644 100644 (file)
@@ -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',
     },
 ]