mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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
This commit is contained in:
parent
6bb462e26a
commit
f1ed123cad
@ -59,7 +59,6 @@ BuildRequires: m4
|
||||
BuildRequires: libtool
|
||||
BuildRequires: gettext
|
||||
BuildRequires: python-devel
|
||||
BuildRequires: authconfig
|
||||
BuildRequires: python-ldap
|
||||
BuildRequires: python-setuptools
|
||||
BuildRequires: python-krbV
|
||||
@ -79,6 +78,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 +151,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 +221,7 @@ Requires: nss-tools
|
||||
Requires: bind-utils
|
||||
Requires: oddjob-mkhomedir
|
||||
Requires: python-krbV
|
||||
Requires: python-dns
|
||||
|
||||
Obsoletes: ipa-client >= 1.0
|
||||
|
||||
@ -256,7 +258,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 +684,9 @@ fi
|
||||
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
|
||||
|
||||
%changelog
|
||||
* Fri May 11 2012 Martin Kosek <mkosek@redhat.com> - 2.99.0-29
|
||||
- Replace used DNS client library (acutil) with python-dns
|
||||
|
||||
* Tue Apr 10 2012 Rob Crittenden <rcritten@redhat.com> - 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.
|
||||
|
@ -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")
|
||||
|
@ -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,15 @@ 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, break_on_first=False)
|
||||
synced_ntp = False
|
||||
if len(ntp_servers) > 0:
|
||||
if ntp_servers:
|
||||
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()
|
||||
|
@ -20,12 +20,14 @@
|
||||
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
|
||||
realm_to_suffix, format_netloc
|
||||
|
||||
|
||||
NOT_FQDN = -1
|
||||
@ -93,11 +95,12 @@ 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)
|
||||
return (server[0], domain)
|
||||
else:
|
||||
p = domain.find(".")
|
||||
if p == -1: #no ldap server found and last component of the domain already tested
|
||||
@ -148,11 +151,13 @@ class IPADiscovery:
|
||||
if not self.domain: #no ldap server found
|
||||
return NO_LDAP_SERVER
|
||||
else:
|
||||
root_logger.debug("[ipadnssearchldap]")
|
||||
self.server = self.ipadnssearchldap(domain)
|
||||
if self.server:
|
||||
root_logger.debug("Search for LDAP SRV record in %s", domain)
|
||||
server = self.ipadns_search_srv(domain, '_ldap._tcp', 389)
|
||||
if server:
|
||||
self.server = server[0]
|
||||
self.domain = domain
|
||||
else:
|
||||
self.server = None
|
||||
return NO_LDAP_SERVER
|
||||
|
||||
else: #server forced on us, this means DNS doesn't work :/
|
||||
@ -172,19 +177,16 @@ class IPADiscovery:
|
||||
root_logger.debug("[ipacheckldap]")
|
||||
# We may have received multiple servers corresponding to the domain
|
||||
# Iterate through all of those to check if it is IPA LDAP server
|
||||
servers = parse_items(self.server)
|
||||
ldapret = [NOT_IPA_SERVER]
|
||||
ldapaccess = True
|
||||
for server in servers:
|
||||
if self.server:
|
||||
# check ldap now
|
||||
ldapret = self.ipacheckldap(server, self.realm)
|
||||
ldapret = self.ipacheckldap(self.server, self.realm)
|
||||
|
||||
if ldapret[0] == 0:
|
||||
self.server = ldapret[1]
|
||||
self.realm = ldapret[2]
|
||||
break
|
||||
|
||||
if ldapret[0] == NO_ACCESS_TO_LDAP:
|
||||
elif ldapret[0] == NO_ACCESS_TO_LDAP:
|
||||
ldapaccess = False
|
||||
|
||||
# If one of LDAP servers checked rejects access (may be anonymous
|
||||
@ -310,46 +312,43 @@ class IPADiscovery:
|
||||
os.rmdir(temp_ca_dir)
|
||||
|
||||
|
||||
def ipadnssearchldap(self, tdomain):
|
||||
servers = ""
|
||||
rserver = ""
|
||||
def ipadns_search_srv(self, domain, srv_record_name, default_port,
|
||||
break_on_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 break_on_first: break on the first find and return just one
|
||||
entry
|
||||
"""
|
||||
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
|
||||
qname = '%s.%s' % (srv_record_name, domain)
|
||||
|
||||
return servers
|
||||
root_logger.debug("Search DNS for SRV record of %s", qname)
|
||||
|
||||
def ipadnssearchntp(self, tdomain):
|
||||
servers = ""
|
||||
rserver = ""
|
||||
try:
|
||||
answers = resolver.query(qname, rdatatype.SRV)
|
||||
except DNSException, e:
|
||||
root_logger.debug("DNS record not found: %s", e.__class__.__name__)
|
||||
answers = []
|
||||
|
||||
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)
|
||||
|
||||
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 not server:
|
||||
root_logger.debug("Cannot parse the hostname from SRV record: %s", answer)
|
||||
continue
|
||||
if default_port is not None and answer.port != default_port:
|
||||
server = "%s:%s" % (server, str(answer.port))
|
||||
servers.append(server)
|
||||
if break_on_first:
|
||||
break
|
||||
|
||||
return servers
|
||||
@ -359,35 +358,32 @@ class IPADiscovery:
|
||||
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,
|
||||
break_on_first=False)
|
||||
|
||||
if not kdc:
|
||||
root_logger.debug("SRV record for KDC not found! Realm: %s, SRV record: %s" % (realm, qname))
|
||||
kdc = None
|
||||
kdc = ','.join(kdc)
|
||||
|
||||
return [realm, kdc]
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
)
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
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)
|
||||
# try once with REALM -> domain
|
||||
domain = str(config.default_realm).lower()
|
||||
name = "_ldap._tcp." + domain
|
||||
|
||||
config.default_domain = dom_name
|
||||
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
|
||||
|
||||
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
|
||||
|
@ -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)
|
@ -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
|
||||
@ -611,17 +613,6 @@ def ipa_generate_password(characters=None,pwd_len=None):
|
||||
rndpwd += rndchar
|
||||
return rndpwd
|
||||
|
||||
def parse_items(text):
|
||||
'''Given text with items separated by whitespace or comma, return a list of those items
|
||||
|
||||
The returned list only contains non-empty items.
|
||||
'''
|
||||
split_re = re.compile('[ ,\t\n]+')
|
||||
items = split_re.split(text)
|
||||
for item in items[:]:
|
||||
if not item: items.remove(item)
|
||||
return items
|
||||
|
||||
def user_input(prompt, default = None, allow_empty = True):
|
||||
if default == None:
|
||||
while True:
|
||||
@ -747,6 +738,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.
|
||||
|
@ -30,9 +30,11 @@ import netaddr
|
||||
import time
|
||||
import tempfile
|
||||
import shutil
|
||||
from ConfigParser import SafeConfigParser
|
||||
from dns import resolver, rdatatype
|
||||
from dns.exception import DNSException
|
||||
|
||||
from ipapython import ipautil, dnsclient, sysrestore
|
||||
from ConfigParser import SafeConfigParser
|
||||
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"):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user