-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

This is a first patch towards IPv6 support. Currently it only touches
the installer only as other changes will be fully testable only when
python-nss is IPv6 ready.

Changes include:
 * parse AAAA records in dnsclient
 * also ask for AAAA records when verifying FQDN
 * do not use functions that are not IPv6 aware - notably
   socket.gethostbyname(). The complete list of functions was taken
   from http://www.akkadia.org/drepper/userapi-ipv6.html
   section "Interface Checklist"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAk0HR8IACgkQHsardTLnvCU/jQCePrBXG+2NTDmfq1y3BgQIaHMl
eH8AnAivy5jA3YQP1JXznBg/IubD3lLG
=m52C
-----END PGP SIGNATURE-----
From 66376ec364e5a5f5d42492d42412b4ea0893ea99 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Wed, 1 Dec 2010 17:22:56 +0100
Subject: [PATCH] Make the IPA installer IPv6 friendly

Notable changes include:
 * parse AAAA records in dnsclient
 * also ask for AAAA records when verifying FQDN
 * do not use functions that are not IPv6 aware - notably socket.gethostbyname()
   The complete list of functions was taken from http://www.akkadia.org/drepper/userapi-ipv6.html
   section "Interface Checklist"
---
 install/tools/ipa-dns-install     |   21 ++++---
 install/tools/ipa-replica-install |   14 +++--
 install/tools/ipa-server-install  |   27 +++++----
 ipapython/dnsclient.py            |   19 ++++++-
 ipaserver/install/installutils.py |  109 +++++++++++++++++++++++--------------
 ipaserver/install/krbinstance.py  |    2 +-
 6 files changed, 121 insertions(+), 71 deletions(-)

diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index bf6679e..a91938f 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -62,16 +62,19 @@ def parse_options():
 def resolve_host(host_name):
     ip = None
     try:
-        ip = socket.gethostbyname(host_name)
-
-        if ip == "127.0.0.1" or ip == "::1":
-            print "The hostname resolves to the localhost address (127.0.0.1/::1)"
-            print "Please change your /etc/hosts file so that the hostname"
-            print "resolves to the ip address of your network interface."
-            print ""
-            print "Please fix your /etc/hosts file and restart the setup program"
-            return None
-
+        addrinfos = socket.getaddrinfo(host_name, None,
+                                       socket.AF_UNSPEC, socket.SOCK_DGRAM)
+        for ai in addrinfos:
+            ip = ai[4][0]
+            if ip == "127.0.0.1" or ip == "::1":
+                print "The hostname resolves to the localhost address (127.0.0.1/::1)"
+                print "Please change your /etc/hosts file so that the hostname"
+                print "resolves to the ip address of your network interface."
+                print ""
+                print "Please fix your /etc/hosts file and restart the setup program"
+                return None
+
+        ip = addrinfos[0][4][0]
     except:
         print "Unable to lookup the IP address of the provided host"
     return ip
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 0c13ad0..5ff50f1 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -126,12 +126,14 @@ def get_host_name(no_host_dns):
     return hostname
 
 def resolve_host(host_name):
-    ip = socket.gethostbyname(host_name)
-
-    if ip == "127.0.0.1" or ip == "::1":
-        raise HostnameLocalhost
-
-    return ip
+    addrinfos = socket.getaddrinfo(host_name, None,
+                                   socket.AF_UNSPEC, socket.SOCK_STREAM)
+    for ai in addrinfos:
+        ip = ai[4][0]
+        if ip == "127.0.0.1" or ip == "::1":
+            raise HostnameLocalhost
+
+    return addrinfos[0][4][0]
 
 def set_owner(config, dir):
     pw = pwd.getpwnam(config.ds_user)
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 0a1f1c5..7c1f3c2 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -279,19 +279,22 @@ def read_host_name(host_default,no_host_dns=False):
     return host_name
 
 def resolve_host(host_name):
-    ip = ""
+    ip = None
     try:
-        ip = socket.gethostbyname(host_name)
-
-        if ip == "127.0.0.1" or ip == "::1":
-            print "The hostname resolves to the localhost address (127.0.0.1/::1)"
-            print "Please change your /etc/hosts file so that the hostname"
-            print "resolves to the ip address of your network interface."
-            print "The KDC service does not listen on localhost"
-            print ""
-            print "Please fix your /etc/hosts file and restart the setup program"
-            return None
+        addrinfos = socket.getaddrinfo(host_name, None,
+                                       socket.AF_UNSPEC, socket.SOCK_DGRAM)
+        for ai in addrinfos:
+            ip = ai[4][0]
+            if ip == "127.0.0.1" or ip == "::1":
+                print "The hostname resolves to the localhost address (127.0.0.1/::1)"
+                print "Please change your /etc/hosts file so that the hostname"
+                print "resolves to the ip address of your network interface."
+                print "The KDC service does not listen on localhost"
+                print ""
+                print "Please fix your /etc/hosts file and restart the setup program"
+                return None
 
+        ip = addrinfos[0][4][0]
     except:
         print "Unable to lookup the IP address of the provided host"
     return ip
@@ -554,7 +557,7 @@ def main():
             sys.exit("Aborting installation")
 
     # check the hostname is correctly configured, it must be as the kldap
-    # utilities just use the hostname as returned by gethostbyname to set
+    # utilities just use the hostname as returned by getaddrinfo to set
     # up some of the standard entries
 
     host_default = ""
diff --git a/ipapython/dnsclient.py b/ipapython/dnsclient.py
index 58d93d8..6b02a5e 100644
--- a/ipapython/dnsclient.py
+++ b/ipapython/dnsclient.py
@@ -37,6 +37,7 @@ DNS_T_PTR = 12
 DNS_T_HINFO = 13
 DNS_T_MX = 15
 DNS_T_TXT = 16
+DNS_T_AAAA = 28
 DNS_T_SRV = 33
 DNS_T_ANY = 255
 
@@ -127,6 +128,10 @@ class DNSRData:
 #	u_int32_t address;
 #} dns_rr_a_t;
 #
+#typedef struct dns_rr_aaaa {
+#       unsigned char address[16];
+#} dns_rr_aaaa_t;
+#
 #typedef struct dns_rr_cname {
 #	const char *cname;
 #} dns_rr_cname_t;
@@ -239,6 +244,18 @@ def dnsParseA(data, base):
 		print "A = %d.%d.%d.%d." % (ord(data[0]), ord(data[1]), ord(data[2]), ord(data[3]))
 	return rdata
 
+def dnsParseAAAA(data, base):
+	rdata = DNSRData()
+	if len(data) < 16:
+		rdata.address = 0
+		return None
+
+        rdata.address = list(struct.unpack('!16B', data))
+        if DEBUG_DNSCLIENT:
+            print socket.inet_ntop(socket.AF_INET6,
+                                   struct.pack('!16B', *rdata.address))
+        return rdata
+
 def dnsParseText(data):	
 	if len(data) < 1:
 		return ("", None)
@@ -413,7 +430,7 @@ def dnsParseResults(results):
 			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_SRV: dnsParseSRV}
+			DNS_T_AAAA : dnsParseAAAA, DNS_T_SRV: dnsParseSRV}
 
 		if not rr.dns_type in fmap:
 			if DEBUG_DNSCLIENT:
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 7863f11..ff2e2bd 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -27,6 +27,7 @@ import fileinput
 import sys
 import struct
 import fcntl
+import netaddr
 
 from ipapython import ipautil
 from ipapython import dnsclient
@@ -42,8 +43,65 @@ def get_fqdn():
             fqdn = ""
     return fqdn
 
-def verify_fqdn(host_name,no_host_dns=False):
+def verify_dns_records(host_name, responses, resaddr, family):
+    familykw = { 'ipv4' : {
+                             'dns_type' : dnsclient.DNS_T_A,
+                             'socket_family' : socket.AF_INET,
+                          },
+                 'ipv6' : {
+                             'dns_type' : dnsclient.DNS_T_AAAA,
+                             'socket_family' : socket.AF_INET6,
+                          },
+               }
+
+    family = family.lower()
+    if family not in familykw.keys():
+        raise RuntimeError("Unknown faimily %s\n" % family)
+
+    if len(responses) == 0:
+        raise IOError(errno.ENOENT,
+                      "Warning: Hostname (%s) not found with NSS calls" % host_name)
+
+    rec = None
+    for rsn in responses:
+        if rsn.dns_type == familykw[family]['dns_type']:
+            rec = rsn
+            break
+
+    if rec == None:
+        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))
+    else:
+        familykw[family]['address'] = socket.inet_ntop(socket.AF_INET6,
+                                                       struct.pack('!16B', *rec.rdata.address))
+
+    # 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))
+
+    rs = dnsclient.query(dns_addr.reverse_dns, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
+    if len(rs) == 0:
+        raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
 
+    rev = None
+    for rsn in rs:
+        if rsn.dns_type == dnsclient.DNS_T_PTR:
+            rev = rsn
+            break
+
+    if rev == None:
+        raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
+
+    if rec.dns_name != rev.rdata.ptrdname:
+        raise RuntimeError("The DNS forward record %s does not match the reverse address %s" % (rec.dns_name, rev.rdata.ptrdname))
+
+
+def verify_fqdn(host_name,no_host_dns=False):
     if len(host_name.split(".")) < 2 or host_name == "localhost.localdomain":
         raise RuntimeError("Invalid hostname: " + host_name)
 
@@ -59,7 +117,7 @@ def verify_fqdn(host_name,no_host_dns=False):
         if a[4][0] == '127.0.0.1' or a[4][0] == '::1':
             raise RuntimeError("The IPA Server hostname cannot resolve to localhost (%s). A routable IP address must be used. Check /etc/hosts to see if %s is an alias for %s" % (a[4][0], host_name, a[4][0]))
         try:
-            revaddr = a[4][0]
+            resaddr = a[4][0]
             revname = socket.gethostbyaddr(a[4][0])[0]
         except:
             raise RuntimeError("Unable to resolve the reverse ip address, check /etc/hosts or DNS name resolution")
@@ -77,48 +135,15 @@ def verify_fqdn(host_name,no_host_dns=False):
             if rsn.dns_type == dnsclient.DNS_T_CNAME:
                 raise RuntimeError("The IPA Server Hostname cannot be a CNAME, only A names are allowed.")
 
-    # Verify that it is a DNS A record
+    # Verify that it is a DNS A or AAAA record
     rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
-    if len(rs) == 0:
-        print "Warning: Hostname (%s) not found in DNS" % host_name
-        return
-
-    rec = None
-    for rsn in rs:
-        if rsn.dns_type == dnsclient.DNS_T_A:
-            rec = rsn
-            break
-
-    if rec == None:
-        print "Warning: Hostname (%s) not found in DNS" % host_name
-        return
-
-    # Compare the forward and reverse
-    forward = rec.dns_name
-
-    addr = socket.inet_ntoa(struct.pack('<L',rec.rdata.address))
-    ipaddr = socket.inet_ntoa(struct.pack('!L',rec.rdata.address))
-    if revaddr != ipaddr:
-        raise RuntimeError("The network address %s does not match the reverse lookup %s. Check /etc/hosts and ensure that %s is the IP address for %s" % (ipaddr, revaddr, ipaddr, host_name))
-
-    addr = addr + ".in-addr.arpa."
-    rs = dnsclient.query(addr, dnsclient.DNS_C_IN, dnsclient.DNS_T_PTR)
-    if len(rs) == 0:
-        raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
-
-    rev = None
-    for rsn in rs:
-        if rsn.dns_type == dnsclient.DNS_T_PTR:
-            rev = rsn
-            break
-
-    if rev == None:
-        raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, addr))
-
-    reverse = rev.rdata.ptrdname
+    try:
+        verify_dns_records(host_name, rs, resaddr, 'ipv4')
+    except IOError, e:
+        if e.errno == errno.ENOENT: # retry IPv6
+            rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA)
+            verify_dns_records(host_name, rs, resaddr, 'ipv6')
 
-    if forward != reverse:
-        raise RuntimeError("The DNS forward record %s does not match the reverse address %s" % (forward, reverse))
 
 def verify_ip_address(ip):
     is_ok = True
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 247b390..9447c4a 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -145,7 +145,7 @@ class KrbInstance(service.Service):
         self.fqdn = host_name
         self.realm = realm_name.upper()
         self.host = host_name.split(".")[0]
-        self.ip = socket.gethostbyname(host_name)
+        self.ip = socket.getaddrinfo(host_name, None, socket.AF_UNSPEC, socket.SOCK_STREAM)[0][4][0]
         self.domain = domain_name
         self.suffix = util.realm_to_suffix(self.realm)
         self.kdc_password = ipautil.ipa_generate_password()
-- 
1.7.3.2

Attachment: freeipa-jhrozek-021-Make-the-IPA-installer-IPv6-friendly.patch.sig
Description: PGP signature

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

Reply via email to