diff options
Diffstat (limited to 'src/conf_mode/dns_forwarding.py')
-rwxr-xr-x | src/conf_mode/dns_forwarding.py | 93 |
1 files changed, 34 insertions, 59 deletions
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py index d0d87d73e..c186f47af 100755 --- a/src/conf_mode/dns_forwarding.py +++ b/src/conf_mode/dns_forwarding.py @@ -21,14 +21,12 @@ from sys import exit from glob import glob from vyos.config import Config -from vyos.configdict import dict_merge from vyos.hostsd_client import Client as hostsd_client from vyos.template import render -from vyos.template import is_ipv6 -from vyos.util import call -from vyos.util import chown -from vyos.util import dict_search -from vyos.xml import defaults +from vyos.template import bracketize_ipv6 +from vyos.utils.process import call +from vyos.utils.permission import chown +from vyos.utils.dict import dict_search from vyos import ConfigError from vyos import airbag @@ -52,13 +50,10 @@ def get_config(config=None): if not conf.exists(base): return None - dns = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True) - # We have gathered the dict representation of the CLI, but there are default - # options which we need to update into the dictionary retrieved. - default_values = defaults(base) - # T2665 due to how defaults under tag nodes work, we must clear these out before we merge - del default_values['authoritative_domain'] - dns = dict_merge(default_values, dns) + dns = conf.get_config_dict(base, key_mangling=('-', '_'), + no_tag_node_value_mangle=True, + get_first_key=True, + with_recursive_defaults=True) # some additions to the default dictionary if 'system' in dns: @@ -81,7 +76,7 @@ def get_config(config=None): recorddata = zonedata['records'] - for rtype in [ 'a', 'aaaa', 'cname', 'mx', 'ptr', 'txt', 'spf', 'srv', 'naptr' ]: + for rtype in [ 'a', 'aaaa', 'cname', 'mx', 'ns', 'ptr', 'txt', 'spf', 'srv', 'naptr' ]: if rtype not in recorddata: continue for subnode in recorddata[rtype]: @@ -91,11 +86,8 @@ def get_config(config=None): rdata = recorddata[rtype][subnode] if rtype in [ 'a', 'aaaa' ]: - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - rdata = dict_merge(rdefaults, rdata) - if not 'address' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one address is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one address is required') continue if subnode == 'any': @@ -108,12 +100,9 @@ def get_config(config=None): 'ttl': rdata['ttl'], 'value': address }) - elif rtype in ['cname', 'ptr']: - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - rdata = dict_merge(rdefaults, rdata) - + elif rtype in ['cname', 'ptr', 'ns']: if not 'target' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: target is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: target is required') continue zone['records'].append({ @@ -123,18 +112,12 @@ def get_config(config=None): 'value': '{}.'.format(rdata['target']) }) elif rtype == 'mx': - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - del rdefaults['server'] - rdata = dict_merge(rdefaults, rdata) - if not 'server' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one server is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one server is required') continue for servername in rdata['server']: serverdata = rdata['server'][servername] - serverdefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'server']) # T2665 - serverdata = dict_merge(serverdefaults, serverdata) zone['records'].append({ 'name': subnode, 'type': rtype.upper(), @@ -142,11 +125,8 @@ def get_config(config=None): 'value': '{} {}.'.format(serverdata['priority'], servername) }) elif rtype == 'txt': - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - rdata = dict_merge(rdefaults, rdata) - if not 'value' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one value is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one value is required') continue for value in rdata['value']: @@ -157,11 +137,8 @@ def get_config(config=None): 'value': "\"{}\"".format(value.replace("\"", "\\\"")) }) elif rtype == 'spf': - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - rdata = dict_merge(rdefaults, rdata) - if not 'value' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: value is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: value is required') continue zone['records'].append({ @@ -171,25 +148,18 @@ def get_config(config=None): 'value': '"{}"'.format(rdata['value'].replace("\"", "\\\"")) }) elif rtype == 'srv': - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - del rdefaults['entry'] - rdata = dict_merge(rdefaults, rdata) - if not 'entry' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one entry is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one entry is required') continue for entryno in rdata['entry']: entrydata = rdata['entry'][entryno] - entrydefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'entry']) # T2665 - entrydata = dict_merge(entrydefaults, entrydata) - if not 'hostname' in entrydata: - dns['authoritative_zone_errors'].append('{}.{}: hostname is required for entry {}'.format(subnode, node, entryno)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: hostname is required for entry {entryno}') continue if not 'port' in entrydata: - dns['authoritative_zone_errors'].append('{}.{}: port is required for entry {}'.format(subnode, node, entryno)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: port is required for entry {entryno}') continue zone['records'].append({ @@ -199,19 +169,12 @@ def get_config(config=None): 'value': '{} {} {} {}.'.format(entrydata['priority'], entrydata['weight'], entrydata['port'], entrydata['hostname']) }) elif rtype == 'naptr': - rdefaults = defaults(base + ['authoritative-domain', 'records', rtype]) # T2665 - del rdefaults['rule'] - rdata = dict_merge(rdefaults, rdata) - - if not 'rule' in rdata: - dns['authoritative_zone_errors'].append('{}.{}: at least one rule is required'.format(subnode, node)) + dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one rule is required') continue for ruleno in rdata['rule']: ruledata = rdata['rule'][ruleno] - ruledefaults = defaults(base + ['authoritative-domain', 'records', rtype, 'rule']) # T2665 - ruledata = dict_merge(ruledefaults, ruledata) flags = "" if 'lookup-srv' in ruledata: flags += "S" @@ -263,7 +226,7 @@ def verify(dns): # as a domain will contains dot's which is out dictionary delimiter. if 'domain' in dns: for domain in dns['domain']: - if 'server' not in dns['domain'][domain]: + if 'name_server' not in dns['domain'][domain]: raise ConfigError(f'No server configured for domain {domain}!') if 'dns64_prefix' in dns: @@ -329,7 +292,12 @@ def apply(dns): # sources hc.delete_name_servers([hostsd_tag]) if 'name_server' in dns: - hc.add_name_servers({hostsd_tag: dns['name_server']}) + # 'name_server' is of the form + # {'192.0.2.1': {'port': 53}, '2001:db8::1': {'port': 853}, ...} + # canonicalize them as ['192.0.2.1:53', '[2001:db8::1]:853', ...] + nslist = [(lambda h, p: f"{bracketize_ipv6(h)}:{p['port']}")(h, p) + for (h, p) in dns['name_server'].items()] + hc.add_name_servers({hostsd_tag: nslist}) # delete all nameserver tags hc.delete_name_server_tags_recursor(hc.get_name_server_tags_recursor()) @@ -358,7 +326,14 @@ def apply(dns): # the list and keys() are required as get returns a dict, not list hc.delete_forward_zones(list(hc.get_forward_zones().keys())) if 'domain' in dns: - hc.add_forward_zones(dns['domain']) + zones = dns['domain'] + for domain in zones.keys(): + # 'name_server' is of the form + # {'192.0.2.1': {'port': 53}, '2001:db8::1': {'port': 853}, ...} + # canonicalize them as ['192.0.2.1:53', '[2001:db8::1]:853', ...] + zones[domain]['name_server'] = [(lambda h, p: f"{bracketize_ipv6(h)}:{p['port']}")(h, p) + for (h, p) in zones[domain]['name_server'].items()] + hc.add_forward_zones(zones) # hostsd generates NTAs for the authoritative zones # the list and keys() are required as get returns a dict, not list |