nsupdate: Use authoritative server for zone lookup (#62329)

Using a regular recursive resolver to lookup the zone name might not
work when the zone in question belong to a private/internal
domain. The authoritative server being used on the other hand will
definitely know about the zone(s) it's serving.

This approach is also consistent with the nsupdate module already
querying the specified authoritative server for TTL values.

The reason for the implementation having to loop until finding a
direct match is to account for different SOA responses triggered by
CNAMEs and DNAMEs. The previously used `dns.resolver.zone_for_name()`
function does the same.

Resolves #62052
This commit is contained in:
Andreas Olsson 2019-09-28 19:34:29 +02:00 committed by René Moser
parent ee91714eb2
commit 75dfe6c88a
2 changed files with 28 additions and 8 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- nsupdate - Fix zone name lookup of internal/private zones (https://github.com/ansible/ansible/issues/62052)

View file

@ -203,14 +203,7 @@ class RecordManager(object):
if module.params['zone'] is None: if module.params['zone'] is None:
if module.params['record'][-1] != '.': if module.params['record'][-1] != '.':
self.module.fail_json(msg='record must be absolute when omitting zone parameter') self.module.fail_json(msg='record must be absolute when omitting zone parameter')
self.zone = self.lookup_zone()
try:
self.zone = dns.resolver.zone_for_name(self.module.params['record']).to_text()
except (dns.exception.Timeout, dns.resolver.NoNameservers, dns.resolver.NoRootSOA) as e:
self.module.fail_json(msg='Zone resolver error (%s): %s' % (e.__class__.__name__, to_native(e)))
if self.zone is None:
self.module.fail_json(msg='Unable to find zone, dnspython returned None')
else: else:
self.zone = module.params['zone'] self.zone = module.params['zone']
@ -251,6 +244,31 @@ class RecordManager(object):
return entry return entry
return '"{text}"'.format(text=entry) return '"{text}"'.format(text=entry)
def lookup_zone(self):
name = dns.name.from_text(self.module.params['record'])
while True:
query = dns.message.make_query(name, dns.rdatatype.SOA)
try:
if self.module.params['protocol'] == 'tcp':
lookup = dns.query.tcp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
else:
lookup = dns.query.udp(query, self.module.params['server'], timeout=10, port=self.module.params['port'])
except (socket_error, dns.exception.Timeout) as e:
self.module.fail_json(msg='DNS server error: (%s): %s' % (e.__class__.__name__, to_native(e)))
if lookup.rcode() in [dns.rcode.SERVFAIL, dns.rcode.REFUSED]:
self.module.fail_json(msg='Zone lookup failure: \'%s\' will not respond to queries regarding \'%s\'.' % (
self.module.params['server'], self.module.params['record']))
try:
zone = lookup.authority[0].name
if zone == name:
return zone.to_text()
except IndexError:
pass
try:
name = name.parent()
except dns.name.NoParent:
self.module.fail_json(msg='Zone lookup of \'%s\' failed for unknown reason.' % (self.module.params['record']))
def __do_update(self, update): def __do_update(self, update):
response = None response = None
try: try: