From dc405005f537cf278fd6ddfe6b87060bd13d9a67 Mon Sep 17 00:00:00 2001 From: Petr Spacek Date: Tue, 17 May 2016 17:26:20 +0200 Subject: Move IP address resolution from ipaserver.install.installutils to ipapython.dnsutil This is to make it reusable from other modules and to avoid future code duplication. https://fedorahosted.org/freeipa/ticket/5710 Reviewed-By: Martin Basti --- ipapython/dnsutil.py | 59 +++++++++++++++++++++++++++++++++++++++ ipaserver/install/bindinstance.py | 16 ++++++++--- ipaserver/install/installutils.py | 26 ++++------------- 3 files changed, 76 insertions(+), 25 deletions(-) diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py index 6287e3eef..7fa7646d9 100644 --- a/ipapython/dnsutil.py +++ b/ipapython/dnsutil.py @@ -24,6 +24,9 @@ import copy import six +from ipapython.ipautil import CheckedIPAddress +from ipapython.ipa_log_manager import root_logger + if six.PY3: unicode = str @@ -231,6 +234,62 @@ def inside_auto_empty_zone(name): return False +def resolve_rrsets(fqdn, rdtypes): + """ + Get Resource Record sets for given FQDN. + CNAME chain is followed during resolution + but CNAMEs are not returned in the resulting rrset. + + :returns: + set of dns.rrset.RRset objects, can be empty + if the FQDN does not exist or if none of rrtypes exist + """ + # empty set of rdtypes would always return empty set of rrsets + assert rdtypes, "rdtypes must not be empty" + + if not isinstance(fqdn, DNSName): + fqdn = DNSName(fqdn) + + fqdn = fqdn.make_absolute() + rrsets = set() + for rdtype in rdtypes: + try: + answer = dns.resolver.query(fqdn, rdtype) + root_logger.debug('found %d %s records for %s: %s', + len(answer), rdtype, fqdn, ' '.join( + str(rr) for rr in answer)) + rrsets.add(answer.rrset) + except dns.resolver.NXDOMAIN as ex: + root_logger.debug(ex) + break # no such FQDN, do not iterate + except dns.resolver.NoAnswer as ex: + root_logger.debug(ex) # record type does not exist for given FQDN + except dns.exception.DNSException as ex: + root_logger.error('DNS query for %s %s failed: %s', + fqdn, rdtype, ex) + raise + + return rrsets + + +def resolve_ip_addresses(fqdn): + """Get IP addresses from DNS A/AAAA records for given host. + :returns: + list of IP addresses as CheckedIPAddress objects + """ + rrsets = resolve_rrsets(fqdn, ['A', 'AAAA']) + ip_addresses = set() + for rrset in rrsets: + ip_addresses.update({CheckedIPAddress(ip, # accept whatever is in DNS + parse_netmask=False, + allow_network=True, + allow_loopback=True, + allow_broadcast=True, + allow_multicast=True) + for ip in rrset}) + return ip_addresses + + def check_zone_overlap(zone, raise_on_error=True): root_logger.info("Checking DNS domain %s, please wait ..." % zone) if not isinstance(zone, DNSName): diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index 2a642422a..ec8526a8e 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -908,7 +908,9 @@ class BindInstance(service.Service): if fqdn == self.fqdn: continue - addrs = installutils.resolve_host(fqdn) + addrs = dnsutil.resolve_ip_addresses(fqdn) + # hack, will go away with locations + addrs = [str(addr) for addr in addrs] root_logger.debug("Adding DNS records for master %s" % fqdn) self.__add_master_records(fqdn, addrs) @@ -964,7 +966,9 @@ class BindInstance(service.Service): if dns_zone_exists(zone, self.api): addrs = get_fwd_rr(zone, host, api=self.api) else: - addrs = installutils.resolve_host(fqdn) + addrs = dnsutil.resolve_ip_addresses(fqdn) + # hack, will go away with locations + addrs = [str(addr) for addr in addrs] self.__add_ipa_ca_records(fqdn, addrs, True) @@ -1084,7 +1088,9 @@ class BindInstance(service.Service): if dns_zone_exists(zone, self.api): addrs = get_fwd_rr(zone, host, api=self.api) else: - addrs = installutils.resolve_host(fqdn) + addrs = dnsutil.resolve_ip_addresses(fqdn) + # hack, will go away with locations + addrs = [str(addr) for addr in addrs] self.domain = domain_name @@ -1172,7 +1178,9 @@ class BindInstance(service.Service): if dns_zone_exists(zone, self.api): addrs = get_fwd_rr(zone, host, api=self.api) else: - addrs = installutils.resolve_host(fqdn) + addrs = dnsutil.resolve_ip_addresses(fqdn) + # hack, will go away with locations + addrs = [str(addr) for addr in addrs] for addr in addrs: del_fwd_rr(domain_name, IPA_CA_RECORD, addr, api=self.api) diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 179909543..2a71ef7ac 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -55,6 +55,7 @@ from ipaserver.install import certs, service, sysupgrade from ipaplatform import services from ipaplatform.paths import paths from ipaplatform.tasks import tasks +from ipapython import dnsutil if six.PY3: unicode = str @@ -444,24 +445,6 @@ def create_keytab(path, principal): kadmin("ktadd -k " + path + " " + principal) -def resolve_host(host_name): - try: - addrinfos = socket.getaddrinfo(host_name, None, - socket.AF_UNSPEC, socket.SOCK_STREAM) - - ip_list = [] - - for ai in addrinfos: - ip = ai[4][0] - if ip == "127.0.0.1" or ip == "::1": - raise HostnameLocalhost("The hostname resolves to the localhost address") - - ip_list.append(ip) - - return ip_list - except socket.error: - return [] - def get_host_name(no_host_dns): """ Get the current FQDN from the socket and verify that it is valid. @@ -477,9 +460,10 @@ def get_host_name(no_host_dns): def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses): # Check we have a public IP that is associated with the hostname - try: - hostaddr = resolve_host(host_name) - except HostnameLocalhost: + hostaddr = dnsutil.resolve_ip_addresses(host_name) + if hostaddr.intersection( + {ipautil.CheckedIPAddress(ip, allow_loopback=True) + for ip in ['127.0.0.1', '::1']}): print("The hostname resolves to the localhost address (127.0.0.1/::1)", file=sys.stderr) print("Please change your /etc/hosts file so that the hostname", file=sys.stderr) print("resolves to the ip address of your network interface.", file=sys.stderr) -- cgit