from knot_resolver_manager.datamodel.dns64_config import Dns64, Dns64Strict
from knot_resolver_manager.datamodel.lua_config import Lua, LuaStrict
from knot_resolver_manager.datamodel.network_config import Network, NetworkStrict
+from knot_resolver_manager.datamodel.options_config import Options, OptionsStrict
from knot_resolver_manager.datamodel.server_config import Server, ServerStrict
from knot_resolver_manager.utils import DataParser, DataValidator
class KresConfig(DataParser):
server: Server = Server()
+ options: Options = Options()
network: Network = Network()
dns64: Union[bool, Dns64] = False
lua: Lua = Lua()
class KresConfigStrict(DataValidator):
server: ServerStrict
+ options: OptionsStrict
network: NetworkStrict
dns64: Union[bool, Dns64Strict]
lua: LuaStrict
-- MODULES
modules = {
+ {{ "'rebinding < iterate'," if cfg.options.rebinding_protection }}
+ {{ "'workarounds < iterate'," if cfg.options.violators_workarounds }}
+ {{ "'serve_stale < cache'," if cfg.options.serve_stale }}
{{ "dns64 = '"+cfg.dns64.prefix+"'," if cfg.dns64 }}
}
+-- OPTIONS section
+mode('{{ cfg.options.glue_checking }}')
+option('NO_MINIMIZE', {{ 'false' if cfg.options.qname_minimisation else 'true' }})
+option('ALLOW_LOCAL', {{ 'true' if cfg.options.query_loopback else 'false' }})
+option('REORDER_RR', {{ 'true' if cfg.options.reorder_rrset else 'false' }})
+option('NO_0X20', {{ 'false' if cfg.options.query_case_randomization else 'true' }})
+{{ "modules.unload('priming')" if not cfg.options.query_priming }}
+{{ "modules.unload('detect_time_jump')" if not cfg.options.time_jump_detection }}
+{{ "modules.unload('refuse_nord')" if not cfg.options.refuse_no_rd }}
+
{% endif %}
-- LUA section
{% if cfg.lua.script_file %}
--- /dev/null
+from typing import Union
+
+from knot_resolver_manager.utils import DataParser, DataValidator
+from knot_resolver_manager.utils.types import LiteralEnum
+
+from .types import TimeUnit
+
+GlueCheckingEnum = LiteralEnum["normal", "strict", "permissive"]
+
+
+class Prediction(DataParser):
+ window: TimeUnit = TimeUnit("15m")
+ period: int = 24
+
+
+class Options(DataParser):
+ glue_checking: GlueCheckingEnum = "normal"
+ qname_minimisation: bool = True
+ query_loopback: bool = False
+ reorder_rrset: bool = True
+ query_case_randomization: bool = True
+ query_priming: bool = True
+ rebinding_protection: bool = False
+ refuse_no_rd: bool = True
+ time_jump_detection: bool = True
+ violators_workarounds: bool = False
+ serve_stale: bool = False
+
+ prediction: Union[bool, Prediction] = False
+
+
+class PredictionStrict(DataValidator):
+ window: int
+ period: int
+
+ def _validate(self) -> None:
+ pass
+
+
+class OptionsStrict(DataValidator):
+ glue_checking: GlueCheckingEnum
+ qname_minimisation: bool
+ query_loopback: bool
+ reorder_rrset: bool
+ query_case_randomization: bool
+ query_priming: bool
+ rebinding_protection: bool
+ refuse_no_rd: bool
+ time_jump_detection: bool
+ violators_workarounds: bool
+ serve_stale: bool
+
+ prediction: Union[bool, PredictionStrict]
+
+ def _prediction(self, obj: Options) -> Union[bool, Prediction]:
+ if obj.prediction is True:
+ return Prediction()
+ return obj.prediction
+
+ def _validate(self) -> None:
+ pass
--- /dev/null
+from knot_resolver_manager.datamodel.options_config import Options, OptionsStrict
+from knot_resolver_manager.datamodel.types import TimeUnit
+from knot_resolver_manager.utils.data_parser_validator import Format
+
+yaml = """
+glue-checking: strict
+qname-minimisation: false
+query-loopback: true
+reorder-rrset: false
+query-case-randomization: false
+query-priming: true
+rebinding-protection: false
+refuse-no-rd: false
+time-jump-detection: false
+violators-workarounds: true
+serve-stale: true
+prediction:
+ window: 10m
+ period: 20
+"""
+
+config = Options.from_yaml(yaml)
+strict = OptionsStrict(config)
+
+
+def test_parsing():
+ assert config.glue_checking == "strict"
+ assert config.qname_minimisation == False
+ assert config.query_loopback == True
+ assert config.reorder_rrset == False
+ assert config.query_case_randomization == False
+ assert config.query_priming == True
+ assert config.rebinding_protection == False
+ assert config.refuse_no_rd == False
+ assert config.time_jump_detection == False
+ assert config.violators_workarounds == True
+ assert config.serve_stale == True
+
+ assert config.prediction.window == TimeUnit("10m")
+ assert config.prediction.period == 20
+
+
+def test_validating():
+ assert strict.glue_checking == "strict"
+ assert strict.qname_minimisation == False
+ assert strict.query_loopback == True
+ assert strict.reorder_rrset == False
+ assert strict.query_case_randomization == False
+ assert strict.query_priming == True
+ assert strict.rebinding_protection == False
+ assert strict.refuse_no_rd == False
+ assert strict.time_jump_detection == False
+ assert strict.violators_workarounds == True
+ assert strict.serve_stale == True
+
+ assert strict.prediction.window == 10 * 60
+ assert strict.prediction.period == 20
+
+
+def test_prediction_true_defaults():
+ x = config.copy_with_changed_subtree(Format.JSON, "/prediction", "true")
+ y = OptionsStrict(x)
+
+ assert x.prediction == True
+ assert y.prediction.window == 900
+ assert y.prediction.period == 24