Depends on my patch 188
---
A server may have 2 or more NICs and its hostname may thus resolve
to 2 and more forward addresses. IP address checks in install
scripts does not expect this setup and may fail or crash.

This script adds a support for multiple forward addresses for
a hostname. The install scripts do not crash now. When one IP
address is needed, user is asked to choose from all detected
server IP addresses.

https://fedorahosted.org/freeipa/ticket/2154

>From bc6d8c3407c01afa688012cf2fdffb13947228b3 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Tue, 10 Jan 2012 12:18:15 +0100
Subject: [PATCH] Fix ipa-server-install for dual NICs

A server may have 2 or more NICs and its hostname may thus resolve
to 2 and more forward addresses. IP address checks in install
scripts does not expect this setup and may fail or crash.

This script adds a support for multiple forward addresses for
a hostname. The install scripts do not crash now. When one IP
address is needed, user is asked to choose from all detected
server IP addresses.

https://fedorahosted.org/freeipa/ticket/2154
---
 install/tools/ipa-adtrust-install   |    2 +-
 install/tools/ipa-dns-install       |   13 +++++-
 install/tools/ipa-replica-conncheck |    2 +-
 install/tools/ipa-replica-install   |   27 ++++--------
 ipapython/dnsclient.py              |   78 +++++++++++++++++++---------------
 ipaserver/install/bindinstance.py   |   16 ++++++-
 ipaserver/install/installutils.py   |   58 ++++++++++++++++++--------
 7 files changed, 122 insertions(+), 74 deletions(-)

diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 248ea35eaa86dd59ebbc871b86df780cfd71ccf6..1c5940db87ac22b82c0057eb2a84a25691189432 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -136,7 +136,7 @@ def main():
             ip = ipautil.CheckedIPAddress(options.ip_address, match_local=True)
         else:
             hostaddr = resolve_host(api.env.host)
-            ip = hostaddr and ipautil.CheckedIPAddress(hostaddr, match_local=True)
+            ip = hostaddr and ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
     except Exception, e:
         print "Error: Invalid IP Address %s: %s" % (ip, e)
         ip = None
diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index 5c02c20c05dd74cf8a4134e510114a54b5259031..2c0fa51d786e4882a18e25a02a53e812b47538c6 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -147,7 +147,18 @@ def main():
     else:
         hostaddr = resolve_host(api.env.host)
         try:
-            ip = hostaddr and ipautil.CheckedIPAddress(hostaddr, match_local=True)
+            if len(hostaddr) > 1:
+                print >> sys.stderr, "The server hostname resolves to more than one address:"
+                for addr in hostaddr:
+                    print >> sys.stderr, "  %s" % addr
+
+                if options.unattended:
+                    print >> sys.stderr, "Please use --ip-address option to specify the address"
+                    sys.exit(1)
+                else:
+                    ip = read_ip_address(api.env.host, fstore)
+            else:
+                ip = hostaddr and ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
         except Exception, e:
             print "Error: Invalid IP Address %s: %s" % (ip, e)
             ip = None
diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck
index 882d77d302bef0abd6a9d06d4cd783fcbafeefe0..2622130e7c6f6ceabe6ff8a17e89412089897c5f 100755
--- a/install/tools/ipa-replica-conncheck
+++ b/install/tools/ipa-replica-conncheck
@@ -237,7 +237,7 @@ class PortResponder(threading.Thread):
 def port_check(host, port_list):
     ip = installutils.resolve_host(host)
 
-    if ip is None:
+    if not ip:
         raise RuntimeError("Port check failed! Unable to resolve host name '%s'" % host)
 
     failed_ports = []
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 34c787b1919b89bd1e9ad4aaec1ace7daaebc36e..c1474ecbffc6ac5ccc22b91098dbdb1aa4613f0f 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -200,27 +200,22 @@ def install_bind(config, options):
     else:
         forwarders = ()
     bind = bindinstance.BindInstance(dm_password=config.dirman_password)
-    ip_address = resolve_host(config.host_name)
-    if not ip_address:
-        sys.exit("Unable to resolve IP address for host name")
-    ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
-    ip_address = str(ip)
 
     if options.reverse_zone:
-        if not bindinstance.verify_reverse_zone(options.reverse_zone, ip):
+        if not bindinstance.verify_reverse_zone(options.reverse_zone, config.ip):
             sys.exit(1)
         reverse_zone = bindinstance.normalize_zone(options.reverse_zone)
     else:
-        reverse_zone = bindinstance.find_reverse_zone(ip)
+        reverse_zone = bindinstance.find_reverse_zone(config.ip)
         if reverse_zone is None and not options.no_reverse:
-            reverse_zone = bindinstance.get_reverse_zone_default(ip)
+            reverse_zone = bindinstance.get_reverse_zone_default(config.ip)
             if not options.unattended and bindinstance.create_reverse():
-                reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip)
+                reverse_zone = bindinstance.read_reverse_zone(reverse_zone, config.ip)
 
     if reverse_zone is not None:
         print "Using reverse zone %s" % reverse_zone
 
-    bind.setup(config.host_name, ip_address, config.realm_name,
+    bind.setup(config.host_name, config.ip_address, config.realm_name,
                config.domain_name, forwarders, options.conf_ntp, reverse_zone)
     bind.create_instance()
 
@@ -240,14 +235,9 @@ def install_dns_records(config, options):
                               bind_pw=config.dirman_password,
                               tls_cacertfile=CACERT)
     bind = bindinstance.BindInstance(dm_password=config.dirman_password)
-    ip_address = resolve_host(config.host_name)
-    if not ip_address:
-        sys.exit("Unable to resolve IP address for host name")
-    ip = ipautil.CheckedIPAddress(ip_address, match_local=True)
-    ip_address = str(ip)
-    reverse_zone = bindinstance.find_reverse_zone(ip)
+    reverse_zone = bindinstance.find_reverse_zone(config.ip)
 
-    bind.add_master_dns_records(config.host_name, ip_address,
+    bind.add_master_dns_records(config.host_name, config.ip_address,
                                 config.realm_name, config.domain_name,
                                 reverse_zone, options.conf_ntp)
 
@@ -341,7 +331,8 @@ def main():
         replica_conn_check(config.master_host_name, config.host_name, config.realm_name, options.setup_ca, options.admin_password)
 
     # check replica host IP resolution
-    ip = installutils.get_server_ip_address(config.host_name, fstore, True, options)
+    config.ip = installutils.get_server_ip_address(config.host_name, fstore, True, options)
+    config.ip_address = str(config.ip)
 
     # Create the management framework config file
     # Note: We must do this before bootstraping and finalizing ipalib.api
diff --git a/ipapython/dnsclient.py b/ipapython/dnsclient.py
index 9a59d9b99ef0e6c6b5754cd4f18bf9162d39f0b3..3f08866a6403c3f2616ec954ef5c70aa4c849a92 100644
--- a/ipapython/dnsclient.py
+++ b/ipapython/dnsclient.py
@@ -40,6 +40,11 @@ DNS_T_AAAA = 28
 DNS_T_SRV = 33
 DNS_T_ANY = 255
 
+DNS_S_QUERY = 1
+DNS_S_ANSWER = 2
+DNS_S_AUTHORITY = 3
+DNS_S_ADDITIONAL = 4
+
 DEBUG_DNSCLIENT = False
 
 class DNSQueryHeader:
@@ -105,6 +110,7 @@ class DNSResult:
 		self.dns_ttl = 0
 		self.dns_rlength = 0
 		self.rdata = None
+		self.section = None
 
 	def unpack(self, data):
 		(self.dns_type, self.dns_class, self.dns_ttl,
@@ -398,47 +404,51 @@ def dnsParseResults(results):
 			print "Queried for '%s', class = %d, type = %d." % (label,
 				qq.dns_class, qq.dns_type)
 
-	for i in xrange(header.dns_ancount + header.dns_nscount + header.dns_arcount):
-		(rest, label) = dnsParseLabel(rest, results)
-		if label is None:
-			return []
+	for (rec_count, section_id) in ((header.dns_ancount, DNS_S_ANSWER),
+									(header.dns_nscount, DNS_S_AUTHORITY),
+									(header.dns_arcount, DNS_S_ADDITIONAL)):
+		for i in xrange(rec_count):
+			(rest, label) = dnsParseLabel(rest, results)
+			if label is None:
+				return []
 
-		rr = DNSResult()
+			rr = DNSResult()
 
-		rr.dns_name = label
+			rr.dns_name = label
+			rr.section = section_id
 
-		if len(rest) < rr.size():
-			return []
+			if len(rest) < rr.size():
+				return []
 
-		rr.unpack(rest)
-		
-		rest = rest[rr.size():]
+			rr.unpack(rest)
 
-		if DEBUG_DNSCLIENT:
-			print "Answer %d for '%s', class = %d, type = %d, ttl = %d." % (i,
-				rr.dns_name, rr.dns_class, rr.dns_type,
-				rr.dns_ttl)
+			rest = rest[rr.size():]
 
-		if len(rest) < rr.dns_rlength:
 			if DEBUG_DNSCLIENT:
-				print "Answer too short."
-			return []
-		
-		fmap = { DNS_T_A: dnsParseA, DNS_T_NS: dnsParseNS,
-			DNS_T_CNAME: dnsParseCNAME, DNS_T_SOA: dnsParseSOA,
-			DNS_T_NULL: dnsParseNULL, DNS_T_WKS: dnsParseWKS,
-			DNS_T_PTR: dnsParsePTR, DNS_T_HINFO: dnsParseHINFO,
-			DNS_T_MX: dnsParseMX, DNS_T_TXT: dnsParseTXT,
-			DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
-
-		if not rr.dns_type in fmap:
-			if DEBUG_DNSCLIENT:
-				print "Don't know how to parse RR type %d!" %	rr.dns_type
-		else:
-			rr.rdata = fmap[rr.dns_type](rest[:rr.dns_rlength], results)
-
-		rest = rest[rr.dns_rlength:]
-		rrlist += [rr]
+				print "Answer %d for '%s', class = %d, type = %d, ttl = %d." % (i,
+					rr.dns_name, rr.dns_class, rr.dns_type,
+					rr.dns_ttl)
+
+			if len(rest) < rr.dns_rlength:
+				if DEBUG_DNSCLIENT:
+					print "Answer too short."
+				return []
+
+			fmap = { DNS_T_A: dnsParseA, DNS_T_NS: dnsParseNS,
+				DNS_T_CNAME: dnsParseCNAME, DNS_T_SOA: dnsParseSOA,
+				DNS_T_NULL: dnsParseNULL, DNS_T_WKS: dnsParseWKS,
+				DNS_T_PTR: dnsParsePTR, DNS_T_HINFO: dnsParseHINFO,
+				DNS_T_MX: dnsParseMX, DNS_T_TXT: dnsParseTXT,
+				DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
+
+			if not rr.dns_type in fmap:
+				if DEBUG_DNSCLIENT:
+					print "Don't know how to parse RR type %d!" %	rr.dns_type
+			else:
+				rr.rdata = fmap[rr.dns_type](rest[:rr.dns_rlength], results)
+
+			rest = rest[rr.dns_rlength:]
+			rrlist += [rr]
 
 	return rrlist
 
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index c7e382822c47586069f2189a37a33677330c4dda..6e6c941114831a4fb466610e8c0b714a07955f6c 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -197,7 +197,13 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
             raise errors.NotFound("No IPA server with DNS support found!")
         ns_main = dns_masters.pop(0)
         ns_replicas = dns_masters
-        ns_ip_address = resolve_host(ns_main)
+        addresses = resolve_host(ns_main)
+
+        if len(addresses) > 0:
+            # use the first address
+            ns_ip_address = addresses[0]
+        else:
+            ns_ip_address = None
     else:
         ns_main = ns_hostname
         ns_replicas = []
@@ -230,7 +236,13 @@ def add_reverse_zone(zone, ns_hostname=None, ns_ip_address=None,
             raise errors.NotFound("No IPA server with DNS support found!")
         ns_main = dns_masters.pop(0)
         ns_replicas = dns_masters
-        ns_ip_address = resolve_host(ns_main)
+        addresses = resolve_host(ns_main)
+
+        if len(addresses) > 0:
+            # use the first address
+            ns_ip_address = addresses[0]
+        else:
+            ns_ip_address = None
     else:
         ns_main = ns_hostname
         ns_replicas = []
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index e2cabf69b6d90c20daf8848d69a062b301b2204e..6a9f8dbb500a8ab2a6cf36d8e9b81a1acf777761 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -90,27 +90,35 @@ def verify_dns_records(host_name, responses, resaddr, family):
     if family not in familykw.keys():
         raise RuntimeError("Unknown faimily %s\n" % family)
 
-    rec = None
+    rec_list = []
     for rsn in responses:
-        if rsn.dns_type == familykw[family]['dns_type']:
-            rec = rsn
-            break
+        if rsn.section == dnsclient.DNS_S_ANSWER and \
+                rsn.dns_type == familykw[family]['dns_type']:
+            rec_list.append(rsn)
 
-    if rec == None:
+    if not rec_list:
         raise IOError(errno.ENOENT,
                       "Warning: Hostname (%s) not found in DNS" % host_name)
 
     if family == 'ipv4':
-        familykw[family]['address'] = socket.inet_ntop(socket.AF_INET,
-                                                       struct.pack('!L',rec.rdata.address))
+        familykw[family]['address'] = [socket.inet_ntop(socket.AF_INET,
+                                                        struct.pack('!L',rec.rdata.address)) \
+                                                                for rec in rec_list]
     else:
-        familykw[family]['address'] = socket.inet_ntop(socket.AF_INET6,
-                                                       struct.pack('!16B', *rec.rdata.address))
+        familykw[family]['address'] = [socket.inet_ntop(socket.AF_INET6,
+                                                        struct.pack('!16B', *rec.rdata.address)) \
+                                                                for rec in rec_list]
 
     # Check that DNS address is the same is address returned via standard glibc calls
-    dns_addr = netaddr.IPAddress(familykw[family]['address'])
-    if dns_addr.format() != resaddr:
-        raise RuntimeError("The network address %s does not match the DNS lookup %s. Check /etc/hosts and ensure that %s is the IP address for %s" % (dns_addr.format(), resaddr, dns_addr.format(), host_name))
+    dns_addrs = [netaddr.IPAddress(addr) for addr in familykw[family]['address']]
+    dns_addr = None
+    for addr in dns_addrs:
+        if addr.format() == resaddr:
+            dns_addr = addr
+            break
+
+    if dns_addr is None:
+        raise RuntimeError("Host address %s does not match any address in DNS lookup."  % resaddr)
 
     rs = dnsclient.query(dns_addr.reverse_dns, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
     if len(rs) == 0:
@@ -498,14 +506,19 @@ 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")
 
-        return addrinfos[0][4][0]
-    except:
-        return None
+            ip_list.append(ip)
+
+        return ip_list
+    except socket.error:
+        return []
 
 def get_host_name(no_host_dns):
     """
@@ -534,8 +547,19 @@ def get_server_ip_address(host_name, fstore, unattended, options):
         sys.exit(1)
 
     ip_add_to_hosts = False
-    if hostaddr is not None:
-        ip = ipautil.CheckedIPAddress(hostaddr, match_local=True)
+
+    if len(hostaddr) > 1:
+        print >> sys.stderr, "The server hostname resolves to more than one address:"
+        for addr in hostaddr:
+            print >> sys.stderr, "  %s" % addr
+
+        if unattended:
+            print >> sys.stderr, "Please use --ip-address option to specify the address"
+            sys.exit(1)
+        else:
+            ip = read_ip_address(host_name, fstore)
+    elif len(hostaddr) == 1:
+        ip = ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
     else:
         # hostname is not resolvable
         ip = options.ip_address
-- 
1.7.7.5

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to