On Tue, 2012-05-15 at 14:02 +0200, Petr Viktorin wrote:
> On 05/11/2012 06:52 PM, Martin Kosek wrote:
> > python-dns is very feature-rich and it can help us a lot with our DNS
> > related code. This patch does the first step, i.e. replaces acutil use
> > with python-dns, which is more convenient to use as you will see in the
> > patch. More integration will follow in the future.
> >
> > I send this patch rather early, so that I can get responses to this
> > patch early and also so that we are able to catch issues in a safe
> > distance from the next release.
>
> > ---
> > IPA client and server tool set used authconfig acutil module to
> > for client DNS operations. This is not optimal DNS interface for
> > several reasons:
> > - does not provide native Python object oriented interface
> > but but rather C-like interface based on functions and
> > structures which is not easy to use and extend
> > - acutil is not meant to be used by third parties besides
> > authconfig and thus can break without notice
> >
> > Replace the acutil with python-dns package which has a feature rich
> > interface for dealing with all different aspects of DNS including
> > DNSSEC. The main target of this patch is to replace all uses of
> > acutil DNS library with a use python-dns. In most cases, even
> > though the larger parts of the code are changed, the actual
> > functionality is changed only in the following cases:
> > - redundant DNS checks were removed from verify_fqdn function
> > in installutils to make the whole DNS check simpler and
> > less error-prone. Logging was improves for the remaining
> > checks
> > - improved logging for ipa-client-install DNS discovery
> >
> > https://fedorahosted.org/freeipa/ticket/2730
>
> Also relevant: https://fedorahosted.org/freeipa/ticket/1837
Yup, added to commit message.
>
> There is a forgotten acutil reference in a comment in
> install/tools/ipa-dns-install:226
Fixed.
>
> I did find some style issues/suggestions. Stop me if the bikeshedding is
> too useless :)
>
> [...]
> > diff --git a/ipa-client/ipaclient/ipadiscovery.py
> > b/ipa-client/ipaclient/ipadiscovery.py
> > index
> > 86bef28b2d7fdfc8111b493bddec7ac6888f021a..800b4ef9a6396884a1fe2e93bdf94e948f35c57e
> > 100644
> > --- a/ipa-client/ipaclient/ipadiscovery.py
> > +++ b/ipa-client/ipaclient/ipadiscovery.py
> > @@ -20,10 +20,12 @@
> > import socket
> > import os
> > from ipapython.ipa_log_manager import *
> > -import ipapython.dnsclient
> > import tempfile
> > import ldap
> > from ldap import LDAPError
> > +from dns import resolver, rdatatype
> > +from dns.exception import DNSException
> > +
> > from ipapython.ipautil import run, CalledProcessError, valid_ip,
> > get_ipa_basedn, \
> > realm_to_suffix, format_netloc, parse_items
> >
> > @@ -311,81 +313,89 @@ class IPADiscovery:
> >
> >
> > def ipadnssearchldap(self, tdomain):
> > - servers = ""
> > - rserver = ""
> > + server = ""
> >
> > - qname = "_ldap._tcp."+tdomain
> > - # terminate the name
> > - if not qname.endswith("."):
> > - qname += "."
> > - results = ipapython.dnsclient.query(qname,
> > ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
> > + qname = "_ldap._tcp." + tdomain
> >
> > - for result in results:
> > - if result.dns_type == ipapython.dnsclient.DNS_T_SRV:
> > - rserver = result.rdata.server.rstrip(".")
> > - if result.rdata.port and result.rdata.port != 389:
> > - rserver += ":" + str(result.rdata.port)
> > - if servers:
> > - servers += "," + rserver
> > - else:
> > - servers = rserver
> > - break
> > + root_logger.debug("Search DNS for SRV record of %s", qname)
> >
> > - return servers
> > + try:
> > + answers = resolver.query(qname, rdatatype.SRV)
> > + except DNSException, e:
> > + root_logger.debug("DNS record not found: %s",
> > e.__class__.__name__)
> > + answers = []
> > +
> > + for answer in answers:
> > + root_logger.debug("DNS record found: %s", answer)
> > + server = str(answer.target).rstrip(".")
> > + if answer.port != 389:
> > + server += ":" + str(answer.port)
> > + break
> > + return server
> >
> > def ipadnssearchntp(self, tdomain):
> > - servers = ""
> > - rserver = ""
> > + server = ""
> >
> > - qname = "_ntp._udp."+tdomain
> > - # terminate the name
> > - if not qname.endswith("."):
> > - qname += "."
> > - results = ipapython.dnsclient.query(qname,
> > ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
> > + qname = "_ntp._udp." + tdomain
> >
> > - for result in results:
> > - if result.dns_type == ipapython.dnsclient.DNS_T_SRV:
> > - rserver = result.rdata.server.rstrip(".")
> > - if servers:
> > - servers += "," + rserver
> > - else:
> > - servers = rserver
> > - break
> > + root_logger.debug("Search DNS for SRV record of %s", qname)
> >
> > - return servers
> > + try:
> > + answers = resolver.query(qname, rdatatype.SRV)
> > + except DNSException, e:
> > + root_logger.debug("DNS record not found: %s",
> > e.__class__.__name__)
> > + answers = []
> > +
> > + for answer in answers:
> > + root_logger.debug("DNS record found: %s", answer)
> > + server = str(answer.target).rstrip(".")
> > + break
> > +
> > + return server
>
> These function ars mostly the same, except ipadnssearchntp uses "_ntp"
> instead of "_ldap", and it doesn't take the port into account (which it
> probably should?).
> The last part of ipadnssearchkrb is also very similar.
> Please merge the common parts.
Yup, these parts could be merged - done.
>
> > def ipadnssearchkrb(self, tdomain):
>
> [...]
>
> > diff --git a/ipapython/config.py b/ipapython/config.py
> > index
> > d4c724dc9ac754cb221fe60d7c13bd0c716dd296..e06f51a318a2b20c53a8c6933c43d58c34075407
> > 100644
> > --- a/ipapython/config.py
> > +++ b/ipapython/config.py
> [...]
> > @@ -153,7 +155,7 @@ def __parse_config(discover_server = True):
> > try:
> > s = p.get("global", "xmlrpc_uri")
> > server = urlparse.urlsplit(s)
> > - config.default_server.extend(server.netloc)
> > + config.default_server.append(server.netloc)
>
> This is unrelated to the DNS library replacement, right? It should go in
> a separate patch, in case e.g. the big patch gets reverted.
Ok.
>
> [...]
> > + try:
> > + servers = resolver.query(name, rdatatype.SRV)
> > + domain_ok = True
> > + except DNSException:
> > + pass
> > +
> > + if not domain_ok:
> > + # try cycling on domain components of FQDN
>
> This can be just:
>
> try:
> servers = resolver.query(name, rdatatype.SRV)
> domain_ok = True
> except DNSException:
> # try cycling on domain components of FQDN
>
> The extra if and flag variable just makes it less clear.
>
> > + try:
> > + domain = dns.name.from_text(socket.getfqdn())
> > + except DNSException:
> > return False
> > - dom_name = dom_name[tok+1:]
> > - name = "_ldap._tcp." + dom_name + "."
> > - rs = ipapython.dnsclient.query(name,
> > ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
> > - rl = len(rs)
> >
> > - config.default_domain = dom_name
> > + while not domain_ok:
> > + domain = domain.parent()
> > +
> > + if str(domain) == '.':
> > + return False
> > + name = "_ldap._tcp.%s" % domain
> > + try:
> > + servers = resolver.query(name, rdatatype.SRV)
> > + domain_ok = True
> > + except DNSException:
> > + pass
> > +
> > + config.default_domain = str(domain).rstrip(".")
>
> Drop the `domain_ok` variable and just use a while True/break.
> The code flow would be more clear that way, IMO.
>
> [...]
>
> > diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
> > index
> > 4a9db11e23c1b9ab76c9fce9150bc1546426452f..71be39132ea40172595e026a9815acc58d29f4ff
> > 100644
> > --- a/ipapython/ipautil.py
> > +++ b/ipapython/ipautil.py
> [...]
> > +def is_host_resolvable(fqdn):
> > + found = False
> > + for rdtype in (rdatatype.A, rdatatype.AAAA):
> > + try:
> > + resolver.query(fqdn, rdtype)
> > + found = True
> > + except DNSException:
> > + continue
> > +
> > + return found
> > +
>
> This seems too complicated; why not return directly?
>
> for rdtype in rdatatype.A, rdatatype.AAAA:
> try:
> resolver.query(fqdn, rdtype)
> except DNSException:
> pass
> else:
> return True
>
>
> return False
>
> > def get_ipa_basedn(conn):
> > """
> > Get base DN of IPA suffix in given LDAP server.
> > diff --git a/ipaserver/install/installutils.py
> > b/ipaserver/install/installutils.py
> > index
> > 3e7ae41b5fdbc11353e43a63424f19fbc331435a..76b54c94c9d1dad169545dd30c0b9a926f19a3c6
> > 100644
> > --- a/ipaserver/install/installutils.py
> > +++ b/ipaserver/install/installutils.py
> [...]
> > +
> > + # list of verified addresses to prevent multiple searches for the same
> > address
> > + verified = []
>
> Use a set for this.
>
> > for a in hostaddr:
> > - if a[4][0] == '127.0.0.1' or a[4][0] == '::1':
> > - raise HostForwardLookupError("The IPA Server hostname must not
> > 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]))
> > + address = a[4][0]
> > + if address in verified:
> > + continue
> [...]
>
>
Fixed.
Martin
>From e968df78b5dc6a1401f12874910973d2cbb1a995 Mon Sep 17 00:00:00 2001
From: Martin Kosek <[email protected]>
Date: Fri, 11 May 2012 14:38:09 +0200
Subject: [PATCH 1/2] Replace DNS client based on acutil with python-dns
IPA client and server tool set used authconfig acutil module to
for client DNS operations. This is not optimal DNS interface for
several reasons:
- does not provide native Python object oriented interface
but but rather C-like interface based on functions and
structures which is not easy to use and extend
- acutil is not meant to be used by third parties besides
authconfig and thus can break without notice
Replace the acutil with python-dns package which has a feature rich
interface for dealing with all different aspects of DNS including
DNSSEC. The main target of this patch is to replace all uses of
acutil DNS library with a use python-dns. In most cases, even
though the larger parts of the code are changed, the actual
functionality is changed only in the following cases:
- redundant DNS checks were removed from verify_fqdn function
in installutils to make the whole DNS check simpler and
less error-prone. Logging was improves for the remaining
checks
- improved logging for ipa-client-install DNS discovery
https://fedorahosted.org/freeipa/ticket/2730
https://fedorahosted.org/freeipa/ticket/1837
---
freeipa.spec.in | 7 +-
install/tools/ipa-dns-install | 2 +-
ipa-client/ipa-install/ipa-client-install | 23 +-
ipa-client/ipaclient/ipadiscovery.py | 117 ++++----
ipa-client/ipaclient/ntpconf.py | 14 +-
ipalib/plugins/dns.py | 14 +-
ipalib/rpc.py | 21 +-
ipalib/util.py | 16 +-
ipapython/README | 3 +-
ipapython/config.py | 62 +++--
ipapython/dnsclient.py | 469 -----------------------------
ipapython/ipautil.py | 13 +
ipaserver/install/installutils.py | 120 ++------
13 files changed, 185 insertions(+), 696 deletions(-)
delete mode 100644 ipapython/dnsclient.py
diff --git a/freeipa.spec.in b/freeipa.spec.in
index a1e43a0ad48a227baee860f4d97ce589d74db37d..3574d1f7e82f64ed1ca7bdafdd058a24f0460c62 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -79,6 +79,7 @@ BuildRequires: python-memcached
BuildRequires: sssd >= 1.8.0
BuildRequires: python-lxml
BuildRequires: python-pyasn1 >= 0.0.9a
+BuildRequires: python-dns
%description
IPA is an integrated solution to provide centrally managed Identity (machine,
@@ -151,6 +152,7 @@ Requires(postun): python systemd-units
Requires(preun): python initscripts chkconfig
Requires(postun): python initscripts chkconfig
%endif
+Requires: python-dns
# We have a soft-requires on bind. It is an optional part of
# IPA but if it is configured we need a way to require versions
@@ -220,6 +222,7 @@ Requires: nss-tools
Requires: bind-utils
Requires: oddjob-mkhomedir
Requires: python-krbV
+Requires: python-dns
Obsoletes: ipa-client >= 1.0
@@ -256,7 +259,6 @@ Group: System Environment/Libraries
%if 0%{?fedora} >= 12 || 0%{?rhel} >= 6
Requires: python-kerberos >= 1.1-3
%endif
-Requires: authconfig
Requires: gnupg
Requires: iproute
Requires: pyOpenSSL
@@ -683,6 +685,9 @@ fi
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
%changelog
+* Fri May 11 2012 Martin Kosek <[email protected]> - 2.99.0-29
+- Replace used DNS client library (acutil) with python-dns
+
* Tue Apr 10 2012 Rob Crittenden <[email protected]> - 2.99.0-28
- Set min for selinux-policy to 3.10.0-110 on F-17 to pick up certmonger
policy for restarting services.
diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
index b540630f4f2782603c31ce1905870c38c9af98ab..10547c3425c0ac3d356665d47c5407e2180b1fab 100755
--- a/install/tools/ipa-dns-install
+++ b/install/tools/ipa-dns-install
@@ -223,7 +223,7 @@ def main():
zone_notif=options.zone_notif)
bind.create_instance()
- # Restart http instance to make sure acutil has the right resolver
+ # Restart http instance to make sure that python-dns has the right resolver
# https://bugzilla.redhat.com/show_bug.cgi?id=800368
http = httpinstance.HTTPInstance(fstore)
service.print_msg("Restarting the web server")
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 67279b3ed8ae8a25e845ccbcce7143efcaf6d467..61be5f705e2c7962dd6b4d3a576b7a036b7dd637 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -25,6 +25,7 @@ try:
import os
import time
import socket
+
from ipapython.ipa_log_manager import *
import tempfile
import getpass
@@ -35,7 +36,6 @@ try:
from ipapython.ipautil import run, user_input, CalledProcessError, file_exists, realm_to_suffix
import ipapython.services as ipaservices
from ipapython import ipautil
- from ipapython import dnsclient
from ipapython import sysrestore
from ipapython import version
from ipapython import certmonger
@@ -996,18 +996,10 @@ def update_dns(server, hostname):
def client_dns(server, hostname, dns_updates=False):
- dns_ok = False
+ dns_ok = ipautil.is_host_resolvable(hostname)
- # Check if the client has an A record registered in its name.
- rs = dnsclient.query(hostname+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
- if len([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0:
- dns_ok = True
- else:
- rs = dnsclient.query(hostname+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA)
- if len([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0:
- dns_ok = True
- else:
- print "Warning: Hostname (%s) not found in DNS" % hostname
+ if not dns_ok:
+ print "Warning: Hostname (%s) not found in DNS" % hostname
if dns_updates or not dns_ok:
update_dns(server, hostname)
@@ -1243,15 +1235,16 @@ def install(options, env, fstore, statestore):
# We assume that NTP servers are discoverable through SRV records in the DNS
# If that fails, we try to sync directly with IPA server, assuming it runs NTP
print 'Synchronizing time with KDC...'
- ntp_servers = ipautil.parse_items(ds.ipadnssearchntp(cli_domain))
+ ntp_servers = ds.ipadns_search_srv(cli_domain, '_ntp._udp', None, get_first=False)
+ ntp_servers = ipautil.parse_items(ntp_servers)
synced_ntp = False
if len(ntp_servers) > 0:
for s in ntp_servers:
- synced_ntp = ipaclient.ntpconf.synconce_ntp(s)
+ synced_ntp = ipaclient.ntpconf.synconce_ntp(s, debug=True)
if synced_ntp:
break
if not synced_ntp:
- synced_ntp = ipaclient.ntpconf.synconce_ntp(cli_server)
+ synced_ntp = ipaclient.ntpconf.synconce_ntp(cli_server, debug=True)
if not synced_ntp:
print "Unable to sync time with IPA NTP server, assuming the time is in sync."
(krb_fd, krb_name) = tempfile.mkstemp()
diff --git a/ipa-client/ipaclient/ipadiscovery.py b/ipa-client/ipaclient/ipadiscovery.py
index 86bef28b2d7fdfc8111b493bddec7ac6888f021a..1889d1918c01fe0c80a3d56b9a7ef304c5a7d97c 100644
--- a/ipa-client/ipaclient/ipadiscovery.py
+++ b/ipa-client/ipaclient/ipadiscovery.py
@@ -20,10 +20,12 @@
import socket
import os
from ipapython.ipa_log_manager import *
-import ipapython.dnsclient
import tempfile
import ldap
from ldap import LDAPError
+from dns import resolver, rdatatype
+from dns.exception import DNSException
+
from ipapython.ipautil import run, CalledProcessError, valid_ip, get_ipa_basedn, \
realm_to_suffix, format_netloc, parse_items
@@ -93,9 +95,10 @@ class IPADiscovery:
isn't found.
"""
server = None
+ root_logger.debug("Start searching for LDAP SRV record in %s and"
+ " its sub-domains", domain)
while not server:
- root_logger.debug("[ipadnssearchldap("+domain+")]")
- server = self.ipadnssearchldap(domain)
+ server = self.ipadns_search_srv(domain, '_ldap._tcp', 389)
if server:
return (server, domain)
else:
@@ -148,8 +151,8 @@ class IPADiscovery:
if not self.domain: #no ldap server found
return NO_LDAP_SERVER
else:
- root_logger.debug("[ipadnssearchldap]")
- self.server = self.ipadnssearchldap(domain)
+ root_logger.debug("Search for LDAP SRV record in %s", domain)
+ self.server = self.ipadns_search_srv(domain, '_ldap._tcp', 389)
if self.server:
self.domain = domain
else:
@@ -310,84 +313,74 @@ class IPADiscovery:
os.rmdir(temp_ca_dir)
- def ipadnssearchldap(self, tdomain):
- servers = ""
- rserver = ""
+ def ipadns_search_srv(self, domain, srv_record_name, default_port,
+ get_first=True):
+ """
+ Search for SRV records in given domain. When no record is found,
+ en empty string is returned
- qname = "_ldap._tcp."+tdomain
- # terminate the name
- if not qname.endswith("."):
- qname += "."
- results = ipapython.dnsclient.query(qname, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
+ :param domain: Search domain name
+ :param srv_record_name: SRV record name, e.g. "_ldap._tcp"
+ :param default_port: When default_port is not None, it is being
+ checked with the port in SRV record and if they don't
+ match, the port from SRV record is appended to
+ found hostname in this format: "hostname:port"
+ :param get_first: break on first find, otherwise multiple finds
+ separated by ":" may be returned
+ """
+ servers = []
- for result in results:
- if result.dns_type == ipapython.dnsclient.DNS_T_SRV:
- rserver = result.rdata.server.rstrip(".")
- if result.rdata.port and result.rdata.port != 389:
- rserver += ":" + str(result.rdata.port)
- if servers:
- servers += "," + rserver
- else:
- servers = rserver
- break
-
- return servers
+ qname = '%s.%s' % (srv_record_name, domain)
- def ipadnssearchntp(self, tdomain):
- servers = ""
- rserver = ""
+ root_logger.debug("Search DNS for SRV record of %s", qname)
- qname = "_ntp._udp."+tdomain
- # terminate the name
- if not qname.endswith("."):
- qname += "."
- results = ipapython.dnsclient.query(qname, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
+ try:
+ answers = resolver.query(qname, rdatatype.SRV)
+ except DNSException, e:
+ root_logger.debug("DNS record not found: %s", e.__class__.__name__)
+ answers = []
- for result in results:
- if result.dns_type == ipapython.dnsclient.DNS_T_SRV:
- rserver = result.rdata.server.rstrip(".")
- if servers:
- servers += "," + rserver
- else:
- servers = rserver
+ for answer in answers:
+ root_logger.debug("DNS record found: %s", answer)
+ server = str(answer.target).rstrip(".")
+ if default_port is not None and answer.port != default_port:
+ server = "%s:%s" % (server, str(answer.port))
+ servers.append(server)
+ if get_first:
break
- return servers
+ return ",".join(servers)
def ipadnssearchkrb(self, tdomain):
realm = None
kdc = None
# now, check for a Kerberos realm the local host or domain is in
qname = "_kerberos." + tdomain
- # terminate the name
- if not qname.endswith("."):
- qname += "."
- results = ipapython.dnsclient.query(qname, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_TXT)
- for result in results:
- if result.dns_type == ipapython.dnsclient.DNS_T_TXT:
- realm = result.rdata.data
+ root_logger.debug("Search DNS for TXT record of %s", qname)
+
+ try:
+ answers = resolver.query(qname, rdatatype.TXT)
+ except DNSException, e:
+ root_logger.debug("DNS record not found: %s", e.__class__.__name__)
+ answers = []
+
+ for answer in answers:
+ root_logger.debug("DNS record found: %s", answer)
+ if answer.strings:
+ realm = answer.strings[0]
if realm:
break
if realm:
# now fetch server information for the realm
- qname = "_kerberos._udp." + realm.lower()
- # terminate the name
- if not qname.endswith("."):
- qname += "."
- results = ipapython.dnsclient.query(qname, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
- for result in results:
- if result.dns_type == ipapython.dnsclient.DNS_T_SRV:
- qname = result.rdata.server.rstrip(".")
- if result.rdata.port and result.rdata.port != 88:
- qname += ":" + str(result.rdata.port)
- if kdc:
- kdc += "," + qname
- else:
- kdc = qname
+ domain = realm.lower()
+
+ kdc = self.ipadns_search_srv(domain, '_kerberos._udp', 88,
+ get_first=False)
if not kdc:
root_logger.debug("SRV record for KDC not found! Realm: %s, SRV record: %s" % (realm, qname))
+ kdc = None
return [realm, kdc]
diff --git a/ipa-client/ipaclient/ntpconf.py b/ipa-client/ipaclient/ntpconf.py
index e71692f4019bf410d6107a471330edc98146c29c..aa9261cb29e285973d0eae0270de701bb7197497 100644
--- a/ipa-client/ipaclient/ntpconf.py
+++ b/ipa-client/ipaclient/ntpconf.py
@@ -133,7 +133,7 @@ def config_ntp(server_fqdn, fstore = None, sysstore = None):
# Restart ntpd
ipaservices.knownservices.ntpd.restart()
-def synconce_ntp(server_fqdn):
+def synconce_ntp(server_fqdn, debug=False):
"""
Syncs time with specified server using ntpdate.
Primarily designed to be used before Kerberos setup
@@ -142,15 +142,17 @@ def synconce_ntp(server_fqdn):
Returns True if sync was successful
"""
ntpdate="/usr/sbin/ntpdate"
- result = False
if os.path.exists(ntpdate):
# retry several times -- logic follows /etc/init.d/ntpdate
# implementation
+ cmd = [ntpdate, "-U", "ntp", "-s", "-b"]
+ if debug:
+ cmd.append('-d')
+ cmd.append(server_fqdn)
for retry in range(0,3):
try:
- ipautil.run([ntpdate, "-U", "ntp", "-s", "-b", server_fqdn])
- result = True
- break
+ ipautil.run(cmd)
+ return True
except:
pass
- return result
+ return False
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index b0e65ab943c1aece36061255726261f3e32488e0..e26332d46832622ad0321ef3083aec4eef3db1b3 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -30,8 +30,7 @@ from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
from ipalib.util import (validate_zonemgr, normalize_zonemgr,
validate_hostname, validate_dns_label, validate_domain_name)
-from ipapython import dnsclient
-from ipapython.ipautil import valid_ip, CheckedIPAddress
+from ipapython.ipautil import valid_ip, CheckedIPAddress, is_host_resolvable
from ldap import explode_dn
__doc__ = _("""
@@ -2610,17 +2609,8 @@ class dns_resolve(Command):
query = '%s.%s.' % (query, api.env.domain)
if query[-1] != '.':
query = query + '.'
- reca = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
- rec6 = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA)
- records = reca + rec6
- found = False
- for rec in records:
- if rec.dns_type == dnsclient.DNS_T_A or \
- rec.dns_type == dnsclient.DNS_T_AAAA:
- found = True
- break
- if not found:
+ if not is_host_resolvable(query):
raise errors.NotFound(
reason=_('Host \'%(host)s\' not found') % {'host': query}
)
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 04a3f3e35cee62ee3900fc33c6c71fbb0067e882..bd18b6bbf566362e9954bd25235512dda7baaffc 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -39,11 +39,15 @@ import errno
import locale
from xmlrpclib import Binary, Fault, dumps, loads, ServerProxy, Transport, ProtocolError
import kerberos
+from dns import resolver, rdatatype
+from dns.exception import DNSException
+
from ipalib.backend import Connectible
from ipalib.errors import public_errors, PublicError, UnknownError, NetworkError, KerberosError, XMLRPCMarshallError
from ipalib import errors
from ipalib.request import context, Connection
-from ipapython import ipautil, dnsclient
+from ipapython import ipautil
+
import httplib
import socket
from ipapython.nsslib import NSSHTTPS, NSSConnection
@@ -349,11 +353,16 @@ class xmlclient(Connectible):
(scheme, netloc, path, params, query, fragment) = urlparse.urlparse(self.env.xmlrpc_uri)
servers = []
name = '_ldap._tcp.%s.' % self.env.domain
- rs = dnsclient.query(name, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV)
- for r in rs:
- if r.dns_type == dnsclient.DNS_T_SRV:
- rsrv = r.rdata.server.rstrip('.')
- servers.append('https://%s%s' % (ipautil.format_netloc(rsrv), path))
+
+ try:
+ answers = resolver.query(name, rdatatype.SRV)
+ except DNSException, e:
+ answers = []
+
+ for answer in answers:
+ server = str(answer.target).rstrip(".")
+ servers.append('https://%s%s' % (ipautil.format_netloc(server), path))
+
servers = list(set(servers))
# the list/set conversion won't preserve order so stick in the
# local config file version here.
diff --git a/ipalib/util.py b/ipalib/util.py
index 64ac6b2cf2a693f68e4ae5f333b22be16c27d690..50da7432759cb9d1c4fa41a6c303383ba095f777 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -28,11 +28,12 @@ import socket
import re
from types import NoneType
from weakref import WeakKeyDictionary
+from dns import resolver, rdatatype
+from dns.exception import DNSException
from ipalib import errors
from ipalib.text import _
from ipalib.dn import DN, RDN
-from ipapython import dnsclient
from ipapython.ipautil import decode_ssh_pubkey
@@ -88,16 +89,17 @@ def validate_host_dns(log, fqdn):
"""
See if the hostname has a DNS A record.
"""
- rs = dnsclient.query(fqdn + '.', dnsclient.DNS_C_IN, dnsclient.DNS_T_A)
- if len(rs) == 0:
+ try:
+ answers = resolver.query(fqdn, rdatatype.A)
+ log.debug(
+ 'IPA: found %d records for %s: %s' % (len(answers), fqdn,
+ ' '.join(str(answer) for answer in answers))
+ )
+ except DNSException, e:
log.debug(
'IPA: DNS A record lookup failed for %s' % fqdn
)
raise errors.DNSNotARecordError()
- else:
- log.debug(
- 'IPA: found %d records for %s' % (len(rs), fqdn)
- )
def isvalid_base64(data):
"""
diff --git a/ipapython/README b/ipapython/README
index ec2bb3a52cbd08073bfc4851f2fe3d3e3934ea10..a724a7faa4c9a7997b55a620654e31cd16cf8d16 100644
--- a/ipapython/README
+++ b/ipapython/README
@@ -3,10 +3,9 @@ geared currently towards command-line tools.
A brief overview:
-config.py - identify the IPA server domain and realm. It uses dnsclient to
+config.py - identify the IPA server domain and realm. It uses python-dns to
try to detect this information first and will fall back to
/etc/ipa/default.conf if that fails.
-dnsclient.py - find IPA information via DNS
ipautil.py - helper functions
diff --git a/ipapython/config.py b/ipapython/config.py
index d4c724dc9ac754cb221fe60d7c13bd0c716dd296..d428b1e27247d62628333a45e54458ca9195e7d8 100644
--- a/ipapython/config.py
+++ b/ipapython/config.py
@@ -20,9 +20,11 @@
import ConfigParser
from optparse import Option, Values, OptionParser, IndentedHelpFormatter, OptionValueError
from copy import copy
+from dns import resolver, rdatatype
+from dns.exception import DNSException
+import dns.name
import socket
-import ipapython.dnsclient
import re
import urlparse
@@ -163,7 +165,7 @@ def __parse_config(discover_server = True):
pass
def __discover_config(discover_server = True):
- rl = 0
+ servers = []
try:
if not config.default_realm:
try:
@@ -177,34 +179,44 @@ def __discover_config(discover_server = True):
return False
if not config.default_domain:
- #try once with REALM -> domain
- dom_name = str(config.default_realm).lower()
- name = "_ldap._tcp."+dom_name+"."
- rs = ipapython.dnsclient.query(name, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
- rl = len(rs)
- if rl == 0:
- #try cycling on domain components of FQDN
- dom_name = socket.getfqdn()
- while rl == 0:
- tok = dom_name.find(".")
- if tok == -1:
+ # try once with REALM -> domain
+ domain = str(config.default_realm).lower()
+ name = "_ldap._tcp." + domain
+
+ try:
+ servers = resolver.query(name, rdatatype.SRV)
+ except DNSException:
+ # try cycling on domain components of FQDN
+ try:
+ domain = dns.name.from_text(socket.getfqdn())
+ except DNSException:
return False
- dom_name = dom_name[tok+1:]
- name = "_ldap._tcp." + dom_name + "."
- rs = ipapython.dnsclient.query(name, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
- rl = len(rs)
- config.default_domain = dom_name
+ while True:
+ domain = domain.parent()
+
+ if str(domain) == '.':
+ return False
+ name = "_ldap._tcp.%s" % domain
+ try:
+ servers = resolver.query(name, rdatatype.SRV)
+ break
+ except DNSException:
+ pass
+
+ config.default_domain = str(domain).rstrip(".")
if discover_server:
- if rl == 0:
- name = "_ldap._tcp."+config.default_domain+"."
- rs = ipapython.dnsclient.query(name, ipapython.dnsclient.DNS_C_IN, ipapython.dnsclient.DNS_T_SRV)
+ if not servers:
+ name = "_ldap._tcp.%s." % config.default_domain
+ try:
+ servers = resolver.query(name, rdatatype.SRV)
+ except DNSException:
+ pass
- for r in rs:
- if r.dns_type == ipapython.dnsclient.DNS_T_SRV:
- rsrv = r.rdata.server.rstrip(".")
- config.default_server.append(rsrv)
+ for server in servers:
+ hostname = str(server.target).rstrip(".")
+ config.default_server.append(hostname)
except:
pass
diff --git a/ipapython/dnsclient.py b/ipapython/dnsclient.py
deleted file mode 100644
index 3f08866a6403c3f2616ec954ef5c70aa4c849a92..0000000000000000000000000000000000000000
--- a/ipapython/dnsclient.py
+++ /dev/null
@@ -1,469 +0,0 @@
-#
-# Copyright 2001, 2005 Red Hat, Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import struct
-import socket
-import sys
-
-import acutil
-
-DNS_C_IN = 1
-DNS_C_CS = 2
-DNS_C_CHAOS = 3
-DNS_C_HS = 4
-DNS_C_ANY = 255
-
-DNS_T_A = 1
-DNS_T_NS = 2
-DNS_T_CNAME = 5
-DNS_T_SOA = 6
-DNS_T_NULL = 10
-DNS_T_WKS = 11
-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
-
-DNS_S_QUERY = 1
-DNS_S_ANSWER = 2
-DNS_S_AUTHORITY = 3
-DNS_S_ADDITIONAL = 4
-
-DEBUG_DNSCLIENT = False
-
-class DNSQueryHeader:
- FORMAT = "!HBBHHHH"
- def __init__(self):
- self.dns_id = 0
- self.dns_rd = 0
- self.dns_tc = 0
- self.dns_aa = 0
- self.dns_opcode = 0
- self.dns_qr = 0
- self.dns_rcode = 0
- self.dns_z = 0
- self.dns_ra = 0
- self.dns_qdcount = 0
- self.dns_ancount = 0
- self.dns_nscount = 0
- self.dns_arcount = 0
-
- def pack(self):
- return struct.pack(DNSQueryHeader.FORMAT,
- self.dns_id,
- (self.dns_rd & 1) |
- (self.dns_tc & 1) << 1 |
- (self.dns_aa & 1) << 2 |
- (self.dns_opcode & 15) << 3 |
- (self.dns_qr & 1) << 7,
- (self.dns_rcode & 15) |
- (self.dns_z & 7) << 4 |
- (self.dns_ra & 1) << 7,
- self.dns_qdcount,
- self.dns_ancount,
- self.dns_nscount,
- self.dns_arcount)
-
- def unpack(self, data):
- (self.dns_id, byte1, byte2, self.dns_qdcount, self.dns_ancount,
- self.dns_nscount, self.dns_arcount) = struct.unpack(DNSQueryHeader.FORMAT, data[0:self.size()])
- self.dns_rd = byte1 & 1
- self.dns_tc = (byte1 >> 1) & 1
- self.dns_aa = (byte1 >> 2) & 1
- self.dns_opcode = (byte1 >> 3) & 15
- self.dns_qr = (byte1 >> 7) & 1
- self.dns_rcode = byte2 & 15
- self.dns_z = (byte2 >> 4) & 7
- self.dns_ra = (byte1 >> 7) & 1
-
- def size(self):
- return struct.calcsize(DNSQueryHeader.FORMAT)
-
-def unpackQueryHeader(data):
- header = DNSQueryHeader()
- header.unpack(data)
- return header
-
-class DNSResult:
- FORMAT = "!HHIH"
- QFORMAT = "!HH"
- def __init__(self):
- self.dns_name = ""
- self.dns_type = 0
- self.dns_class = 0
- 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,
- self.dns_rlength) = struct.unpack(DNSResult.FORMAT, data[0:self.size()])
-
- def qunpack(self, data):
- (self.dns_type, self.dns_class) = struct.unpack(DNSResult.QFORMAT, data[0:self.qsize()])
-
- def size(self):
- return struct.calcsize(DNSResult.FORMAT)
-
- def qsize(self):
- return struct.calcsize(DNSResult.QFORMAT)
-
-class DNSRData:
- def __init__(self):
- pass
-
-#typedef struct dns_rr_a {
-# 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;
-#
-#typedef struct dns_rr_hinfo {
-# const char *cpu, *os;
-#} dns_rr_hinfo_t;
-#
-#typedef struct dns_rr_mx {
-# u_int16_t preference;
-# const char *exchange;
-#} dns_rr_mx_t;
-#
-#typedef struct dns_rr_null {
-# unsigned const char *data;
-#} dns_rr_null_t;
-#
-#typedef struct dns_rr_ns {
-# const char *nsdname;
-#} dns_rr_ns_t;
-#
-#typedef struct dns_rr_ptr {
-# const char *ptrdname;
-#} dns_rr_ptr_t;
-#
-#typedef struct dns_rr_soa {
-# const char *mname;
-# const char *rname;
-# u_int32_t serial;
-# int32_t refresh;
-# int32_t retry;
-# int32_t expire;
-# int32_t minimum;
-#} dns_rr_soa_t;
-#
-#typedef struct dns_rr_txt {
-# const char *data;
-#} dns_rr_txt_t;
-#
-#typedef struct dns_rr_srv {
-# const char *server;
-# u_int16_t priority;
-# u_int16_t weight;
-# u_int16_t port;
-#} dns_rr_srv_t;
-
-def dnsNameToLabel(name):
- out = ""
- name = name.split(".")
- for part in name:
- out += chr(len(part)) + part
- return out
-
-def dnsFormatQuery(query, qclass, qtype):
- header = DNSQueryHeader()
-
- header.dns_id = 0 # FIXME: id = 0
- header.dns_rd = 1 # don't know why the original code didn't request recursion for non SOA requests
- header.dns_qr = 0 # query
- header.dns_opcode = 0 # standard query
- header.dns_qdcount = 1 # single query
-
- qlabel = dnsNameToLabel(query)
- if not qlabel:
- return ""
-
- out = header.pack() + qlabel
- out += chr(qtype >> 8)
- out += chr(qtype & 0xff)
- out += chr(qclass >> 8)
- out += chr(qclass & 0xff)
-
- return out
-
-def dnsParseLabel(label, base):
- # returns (output, rest)
- if not label:
- return ("", None)
-
- update = 1
- rest = label
- output = ""
- skip = 0
-
- try:
- while ord(rest[0]):
- if ord(rest[0]) & 0xc0:
- rest = base[((ord(rest[0]) & 0x3f) << 8) + ord(rest[1]):]
- if update:
- skip += 2
- update = 0
- continue
- output += rest[1:ord(rest[0]) + 1] + "."
- if update:
- skip += ord(rest[0]) + 1
- rest = rest[ord(rest[0]) + 1:]
- except IndexError:
- return ("", None)
- return (label[skip+update:], output)
-
-def dnsParseA(data, base):
- rdata = DNSRData()
- if len(data) < 4:
- rdata.address = 0
- return None
-
- rdata.address = (ord(data[0])<<24) | (ord(data[1])<<16) | (ord(data[2])<<8) | (ord(data[3])<<0)
-
- if DEBUG_DNSCLIENT:
- 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)
- tlen = ord(data[0])
- if len(data) < tlen + 1:
- return ("", None)
- return (data[tlen+1:], data[1:tlen+1])
-
-def dnsParseNS(data, base):
- rdata = DNSRData()
- (rest, rdata.nsdname) = dnsParseLabel(data, base)
- if DEBUG_DNSCLIENT:
- print "NS DNAME = \"%s\"." % (rdata.nsdname)
- return rdata
-
-def dnsParseCNAME(data, base):
- rdata = DNSRData()
- (rest, rdata.cname) = dnsParseLabel(data, base)
- if DEBUG_DNSCLIENT:
- print "CNAME = \"%s\"." % (rdata.cname)
- return rdata
-
-def dnsParseSOA(data, base):
- rdata = DNSRData()
- format = "!IIIII"
-
- (rest, rdata.mname) = dnsParseLabel(data, base)
- if rdata.mname is None:
- return None
- (rest, rdata.rname) = dnsParseLabel(rest, base)
- if rdata.rname is None:
- return None
- if len(rest) < struct.calcsize(format):
- return None
-
- (rdata.serial, rdata.refresh, rdata.retry, rdata.expire,
- rdata.minimum) = struct.unpack(format, rest[:struct.calcsize(format)])
-
- if DEBUG_DNSCLIENT:
- print "SOA(mname) = \"%s\"." % rdata.mname
- print "SOA(rname) = \"%s\"." % rdata.rname
- print "SOA(serial) = %d." % rdata.serial
- print "SOA(refresh) = %d." % rdata.refresh
- print "SOA(retry) = %d." % rdata.retry
- print "SOA(expire) = %d." % rdata.expire
- print "SOA(minimum) = %d." % rdata.minimum
- return rdata
-
-def dnsParseNULL(data, base):
- # um, yeah
- return None
-
-def dnsParseWKS(data, base):
- return None
-
-def dnsParseHINFO(data, base):
- rdata = DNSRData()
- (rest, rdata.cpu) = dnsParseText(data)
- if rest:
- (rest, rdata.os) = dnsParseText(rest)
- if DEBUG_DNSCLIENT:
- print "HINFO(cpu) = \"%s\"." % rdata.cpu
- print "HINFO(os) = \"%s\"." % rdata.os
- return rdata
-
-def dnsParseMX(data, base):
- rdata = DNSRData()
- if len(data) < 2:
- return None
- rdata.preference = (ord(data[0]) << 8) | ord(data[1])
- (rest, rdata.exchange) = dnsParseLabel(data[2:], base)
- if DEBUG_DNSCLIENT:
- print "MX(exchanger) = \"%s\"." % rdata.exchange
- print "MX(preference) = %d." % rdata.preference
- return rdata
-
-def dnsParseTXT(data, base):
- rdata = DNSRData()
- (rest, rdata.data) = dnsParseText(data)
- if DEBUG_DNSCLIENT:
- print "TXT = \"%s\"." % rdata.data
- return rdata
-
-def dnsParsePTR(data, base):
- rdata = DNSRData()
- (rest, rdata.ptrdname) = dnsParseLabel(data, base)
- if DEBUG_DNSCLIENT:
- print "PTR = \"%s\"." % rdata.ptrdname
- return rdata
-
-def dnsParseSRV(data, base):
- rdata = DNSRData()
- format = "!HHH"
- flen = struct.calcsize(format)
- if len(data) < flen:
- return None
-
- (rdata.priority, rdata.weight, rdata.port) = struct.unpack(format, data[:flen])
- (rest, rdata.server) = dnsParseLabel(data[flen:], base)
- if DEBUG_DNSCLIENT:
- print "SRV(server) = \"%s\"." % rdata.server
- print "SRV(weight) = %d." % rdata.weight
- print "SRV(priority) = %d." % rdata.priority
- print "SRV(port) = %d." % rdata.port
- return rdata
-
-def dnsParseResults(results):
- try:
- header = unpackQueryHeader(results)
- except struct.error:
- return []
-
- if header.dns_qr != 1: # should be a response
- return []
-
- if header.dns_rcode != 0: # should be no error
- return []
-
- rest = results[header.size():]
-
- rrlist = []
-
- for i in xrange(header.dns_qdcount):
- if not rest:
- return []
-
- qq = DNSResult()
-
- (rest, label) = dnsParseLabel(rest, results)
- if label is None:
- return []
-
- if len(rest) < qq.qsize():
- return []
-
- qq.qunpack(rest)
-
- rest = rest[qq.qsize():]
-
- if DEBUG_DNSCLIENT:
- print "Queried for '%s', class = %d, type = %d." % (label,
- qq.dns_class, qq.dns_type)
-
- 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.dns_name = label
- rr.section = section_id
-
- if len(rest) < rr.size():
- return []
-
- rr.unpack(rest)
-
- rest = rest[rr.size():]
-
- 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)
-
- 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
-
-def query(query, qclass, qtype):
- qdata = dnsFormatQuery(query, qclass, qtype)
- if not qdata:
- return []
- answer = acutil.res_send(qdata)
- if not answer:
- return []
- return dnsParseResults(answer)
-
-if __name__ == '__main__':
- DEBUG_DNSCLIENT = True
- print "Sending query."
- rr = query(len(sys.argv) > 1 and sys.argv[1] or "devserv.devel.redhat.com.",
- DNS_C_IN, DNS_T_ANY)
- sys.exit(0)
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 4a9db11e23c1b9ab76c9fce9150bc1546426452f..b759b7dd658481ae14d18fa6b25e56fdb214fb90 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -41,6 +41,8 @@ import re
import xmlrpclib
import datetime
import netaddr
+from dns import resolver, rdatatype
+from dns.exception import DNSException
from ipapython.ipa_log_manager import *
from ipapython import ipavalidate
@@ -747,6 +749,17 @@ def bind_port_responder(port, socket_type=socket.SOCK_STREAM, socket_timeout=Non
finally:
s.close()
+def is_host_resolvable(fqdn):
+ for rdtype in (rdatatype.A, rdatatype.AAAA):
+ try:
+ resolver.query(fqdn, rdtype)
+ except DNSException:
+ continue
+ else:
+ return True
+
+ return False
+
def get_ipa_basedn(conn):
"""
Get base DN of IPA suffix in given LDAP server.
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 3e7ae41b5fdbc11353e43a63424f19fbc331435a..32728bc5fcde42573bdbde9279d6c2262ee2456b 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -30,9 +30,11 @@ import netaddr
import time
import tempfile
import shutil
+from dns import resolver, rdatatype
+from dns.exception import DNSException
+
from ConfigParser import SafeConfigParser
-
-from ipapython import ipautil, dnsclient, sysrestore
+from ipapython import ipautil, sysrestore
from ipapython.ipa_log_manager import *
from ipalib.util import validate_hostname
@@ -76,68 +78,6 @@ def get_fqdn():
fqdn = ""
return fqdn
-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)
-
- rec_list = []
- for rsn in responses:
- if rsn.section == dnsclient.DNS_S_ANSWER and \
- rsn.dns_type == familykw[family]['dns_type']:
- rec_list.append(rsn)
-
- 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)) \
- for rec in rec_list]
- else:
- 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_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:
- raise RuntimeError("Cannot find Reverse Address for %s (%s)" % (host_name, dns_addr.format()))
-
- 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, dns_addr.format()))
-
- 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, local_hostname=True):
"""
Run fqdn checks for given host:
@@ -168,7 +108,9 @@ def verify_fqdn(host_name, no_host_dns=False, local_hostname=True):
if local_hostname:
try:
+ root_logger.debug('Check if %s is a primary hostname for localhost', host_name)
ex_name = socket.gethostbyaddr(host_name)
+ root_logger.debug('Primary hostname for localhost: %s', ex_name[0])
if host_name != ex_name[0]:
raise HostLookupError("The host name %s does not match the primary host name %s. "\
"Please check /etc/hosts or DNS name resolution" % (host_name, ex_name[0]))
@@ -180,43 +122,41 @@ def verify_fqdn(host_name, no_host_dns=False, local_hostname=True):
return
try:
+ root_logger.debug('Search DNS for %s', host_name)
hostaddr = socket.getaddrinfo(host_name, None)
- except:
+ except Exception, e:
+ root_logger.debug('Search failed: %s', e)
raise HostForwardLookupError("Unable to resolve host name, check /etc/hosts or DNS name resolution")
if len(hostaddr) == 0:
raise HostForwardLookupError("Unable to resolve host name, check /etc/hosts or DNS name resolution")
+ # Verify this is NOT a CNAME
+ try:
+ root_logger.debug('Check if %s is not a CNAME', host_name)
+ resolver.query(host_name, rdatatype.CNAME)
+ raise HostReverseLookupError("The IPA Server Hostname cannot be a CNAME, only A and AAAA names are allowed.")
+ except DNSException:
+ pass
+
+ # list of verified addresses to prevent multiple searches for the same address
+ verified = set()
for a in hostaddr:
- if a[4][0] == '127.0.0.1' or a[4][0] == '::1':
- raise HostForwardLookupError("The IPA Server hostname must not 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]))
+ address = a[4][0]
+ if address in verified:
+ continue
+ if address == '127.0.0.1' or address == '::1':
+ raise HostForwardLookupError("The IPA Server hostname must not resolve to localhost (%s). A routable IP address must be used. Check /etc/hosts to see if %s is an alias for %s" % (address, host_name, address))
try:
- resaddr = a[4][0]
- revname = socket.gethostbyaddr(a[4][0])[0]
- except:
+ root_logger.debug('Check reverse address of %s', address)
+ revname = socket.gethostbyaddr(address)[0]
+ except Exception, e:
+ root_logger.debug('Check failed: %s', e)
raise HostReverseLookupError("Unable to resolve the reverse ip address, check /etc/hosts or DNS name resolution")
+ root_logger.debug('Found reverse name: %s', revname)
if revname != host_name:
raise HostReverseLookupError("The host name %s does not match the reverse lookup %s" % (host_name, revname))
-
- # Verify this is NOT a CNAME
- rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_CNAME)
- if len(rs) != 0:
- for rsn in rs:
- if rsn.dns_type == dnsclient.DNS_T_CNAME:
- raise HostReverseLookupError("The IPA Server Hostname cannot be a CNAME, only A and AAAA names are allowed.")
-
- # 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([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0:
- verify_dns_records(host_name, rs, resaddr, 'ipv4')
- return
-
- rs = dnsclient.query(host_name+".", dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA)
- if len([ rec for rec in rs if rec.dns_type is not dnsclient.DNS_T_SOA ]) > 0:
- verify_dns_records(host_name, rs, resaddr, 'ipv6')
- return
- else:
- print "Warning: Hostname (%s) not found in DNS" % host_name
+ verified.add(address)
def record_in_hosts(ip, host_name=None, file="/etc/hosts"):
"""
--
1.7.7.6
>From 9ecf6806628d65c8567caa5e82db83a79f870364 Mon Sep 17 00:00:00 2001
From: Martin Kosek <[email protected]>
Date: Wed, 16 May 2012 09:36:17 +0200
Subject: [PATCH 2/2] Fix default_server configuration in ipapython.config
When default server was being parsed from IPA's default.conf
configuration file, the parsed server was not appended correctly to
the default_server list.
---
ipapython/config.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ipapython/config.py b/ipapython/config.py
index d428b1e27247d62628333a45e54458ca9195e7d8..349c91767976ca5e157dddc560083ce8d81e733d 100644
--- a/ipapython/config.py
+++ b/ipapython/config.py
@@ -155,7 +155,7 @@ def __parse_config(discover_server = True):
try:
s = p.get("global", "xmlrpc_uri")
server = urlparse.urlsplit(s)
- config.default_server.extend(server.netloc)
+ config.default_server.append(server.netloc)
except:
pass
try:
--
1.7.7.6
_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel