-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 12/15/2010 10:55 AM, Jan Zelený wrote: > Jakub Hrozek <[email protected]> wrote: >> 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" > > Nack, the patch doesn't handle situations when host cannot be resolved. > > Jan >
Thanks, it didn't handle the case in ipa-replica-install, now it should catch the exception and return None (and the caller would react upon getting None for the IP address). In krbinstance.py it would still raise an exception, but I think that is OK during instance creation (we surely don't want to print anything). The user would see the error string, anyway.. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/ iEYEARECAAYFAk0KRO0ACgkQHsardTLnvCXAcQCfZgtSWyGo/gCOLPF0Imz0Ogu0 SnEAoOKsG5WTN38lRBr6mYIvDxXC8Vy4 =2QvT -----END PGP SIGNATURE-----
From ba0b989bfd7d970eae7e5c728077e5f01a712ca4 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <[email protected]> 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 | 19 +++++-- install/tools/ipa-server-install | 27 +++++---- ipapython/dnsclient.py | 19 ++++++- ipaserver/install/installutils.py | 109 +++++++++++++++++++++++-------------- ipaserver/install/krbinstance.py | 2 +- 6 files changed, 126 insertions(+), 71 deletions(-) diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install index 7ab5d2c..d4cd1eb 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 d30f53e..d188ce0 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -126,12 +126,17 @@ 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 + try: + 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] + except: + return None def set_owner(config, dir): pw = pwd.getpwnam(config.ds_user) @@ -239,6 +244,8 @@ def install_bind(config, options): 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") create_reverse = bindinstance.create_reverse(options.unattended) bind.setup(config.host_name, ip_address, config.realm_name, config.domain_name, forwarders, options.conf_ntp, create_reverse) diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index 3ec1a3e..6b0c2a2 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 f18b0f3..9a59d9b 100644 --- a/ipapython/dnsclient.py +++ b/ipapython/dnsclient.py @@ -36,6 +36,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 @@ -126,6 +127,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; @@ -238,6 +243,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) @@ -412,7 +429,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 b02e355..ab7bacc 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 c86e38a..6b5eaca 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.3
freeipa-jhrozek-021-02-Make-the-IPA-installer-IPv6-friendly.patch.sig
Description: PGP signature
_______________________________________________ Freeipa-devel mailing list [email protected] https://www.redhat.com/mailman/listinfo/freeipa-devel
