Bring back use of dnspython for A/AAAA resolution.
This is behind a use_dnspython flag, however, so it can be disabled as desired.
This commit is contained in:
parent
02f79fc94b
commit
ad91a8cd5e
@ -32,10 +32,10 @@ log = logging.getLogger(__name__)
|
|||||||
#: cd dnspython
|
#: cd dnspython
|
||||||
#: git checkout python3
|
#: git checkout python3
|
||||||
#: python3 setup.py install
|
#: python3 setup.py install
|
||||||
USE_DNSPYTHON = False
|
DNSPYTHON_AVAILABLE = False
|
||||||
try:
|
try:
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
USE_DNSPYTHON = True
|
DNSPYTHON_AVAILABLE = True
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
log.debug("Could not find dnspython package. " + \
|
log.debug("Could not find dnspython package. " + \
|
||||||
"Not all features will be available")
|
"Not all features will be available")
|
||||||
@ -47,13 +47,13 @@ def default_resolver():
|
|||||||
:returns: A :class:`dns.resolver.Resolver` object if dnspython
|
:returns: A :class:`dns.resolver.Resolver` object if dnspython
|
||||||
is available. Otherwise, ``None``.
|
is available. Otherwise, ``None``.
|
||||||
"""
|
"""
|
||||||
if USE_DNSPYTHON:
|
if DNSPYTHON_AVAILABLE:
|
||||||
return dns.resolver.get_default_resolver()
|
return dns.resolver.get_default_resolver()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def resolve(host, port=None, service=None, proto='tcp',
|
def resolve(host, port=None, service=None, proto='tcp',
|
||||||
resolver=None, use_ipv6=True):
|
resolver=None, use_ipv6=True, use_dnspython=True):
|
||||||
"""Peform DNS resolution for a given hostname.
|
"""Peform DNS resolution for a given hostname.
|
||||||
|
|
||||||
Resolution may perform SRV record lookups if a service and protocol
|
Resolution may perform SRV record lookups if a service and protocol
|
||||||
@ -77,6 +77,9 @@ def resolve(host, port=None, service=None, proto='tcp',
|
|||||||
:param use_ipv6: Optionally control the use of IPv6 in situations
|
:param use_ipv6: Optionally control the use of IPv6 in situations
|
||||||
where it is either not available, or performance
|
where it is either not available, or performance
|
||||||
is degraded. Defaults to ``True``.
|
is degraded. Defaults to ``True``.
|
||||||
|
:param use_dnspython: Optionally control if dnspython is used to make
|
||||||
|
the DNS queries instead of the built-in DNS
|
||||||
|
library.
|
||||||
|
|
||||||
:type host: string
|
:type host: string
|
||||||
:type port: int
|
:type port: int
|
||||||
@ -84,14 +87,22 @@ def resolve(host, port=None, service=None, proto='tcp',
|
|||||||
:type proto: string
|
:type proto: string
|
||||||
:type resolver: :class:`dns.resolver.Resolver`
|
:type resolver: :class:`dns.resolver.Resolver`
|
||||||
:type use_ipv6: bool
|
:type use_ipv6: bool
|
||||||
|
:type use_dnspython: bool
|
||||||
|
|
||||||
:return: An iterable of IP address, port pairs in the order
|
:return: An iterable of IP address, port pairs in the order
|
||||||
dictated by SRV priorities and weights, if applicable.
|
dictated by SRV priorities and weights, if applicable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not use_dnspython:
|
||||||
|
if DNSPYTHON_AVAILABLE:
|
||||||
|
log.debug("DNS: Not using dnspython, but dnspython is installed.")
|
||||||
|
else:
|
||||||
|
log.debug("DNS: Not using dnspython.")
|
||||||
|
|
||||||
if not use_ipv6:
|
if not use_ipv6:
|
||||||
log.debug("DNS: Use of IPv6 has been disabled.")
|
log.debug("DNS: Use of IPv6 has been disabled.")
|
||||||
|
|
||||||
if resolver is None and USE_DNSPYTHON:
|
if resolver is None and DNSPYTHON_AVAILABLE and use_dnspython:
|
||||||
resolver = dns.resolver.get_default_resolver()
|
resolver = dns.resolver.get_default_resolver()
|
||||||
|
|
||||||
# An IPv6 literal is allowed to be enclosed in square brackets, but
|
# An IPv6 literal is allowed to be enclosed in square brackets, but
|
||||||
@ -122,7 +133,9 @@ def resolve(host, port=None, service=None, proto='tcp',
|
|||||||
if not service:
|
if not service:
|
||||||
hosts = [(host, port)]
|
hosts = [(host, port)]
|
||||||
else:
|
else:
|
||||||
hosts = get_SRV(host, port, service, proto, resolver=resolver)
|
hosts = get_SRV(host, port, service, proto,
|
||||||
|
resolver=resolver,
|
||||||
|
use_dnspython=use_dnspython)
|
||||||
|
|
||||||
for host, port in hosts:
|
for host, port in hosts:
|
||||||
results = []
|
results = []
|
||||||
@ -131,16 +144,18 @@ def resolve(host, port=None, service=None, proto='tcp',
|
|||||||
results.append((host, '::1', port))
|
results.append((host, '::1', port))
|
||||||
results.append((host, '127.0.0.1', port))
|
results.append((host, '127.0.0.1', port))
|
||||||
if use_ipv6:
|
if use_ipv6:
|
||||||
for address in get_AAAA(host):
|
for address in get_AAAA(host, resolver=resolver,
|
||||||
|
use_dnspython=use_dnspython):
|
||||||
results.append((host, address, port))
|
results.append((host, address, port))
|
||||||
for address in get_A(host):
|
for address in get_A(host, resolver=resolver,
|
||||||
|
use_dnspython=use_dnspython):
|
||||||
results.append((host, address, port))
|
results.append((host, address, port))
|
||||||
|
|
||||||
for host, address, port in results:
|
for host, address, port in results:
|
||||||
yield host, address, port
|
yield host, address, port
|
||||||
|
|
||||||
|
|
||||||
def get_A(host):
|
def get_A(host, resolver=None, use_dnspython=True):
|
||||||
"""Lookup DNS A records for a given host.
|
"""Lookup DNS A records for a given host.
|
||||||
|
|
||||||
If ``resolver`` is not provided, or is ``None``, then resolution will
|
If ``resolver`` is not provided, or is ``None``, then resolution will
|
||||||
@ -148,9 +163,13 @@ def get_A(host):
|
|||||||
|
|
||||||
:param host: The hostname to resolve for A record IPv4 addresses.
|
:param host: The hostname to resolve for A record IPv4 addresses.
|
||||||
:param resolver: Optional DNS resolver object to use for the query.
|
:param resolver: Optional DNS resolver object to use for the query.
|
||||||
|
:param use_dnspython: Optionally control if dnspython is used to make
|
||||||
|
the DNS queries instead of the built-in DNS
|
||||||
|
library.
|
||||||
|
|
||||||
:type host: string
|
:type host: string
|
||||||
:type resolver: :class:`dns.resolver.Resolver` or ``None``
|
:type resolver: :class:`dns.resolver.Resolver` or ``None``
|
||||||
|
:type use_dnspython: bool
|
||||||
|
|
||||||
:return: A list of IPv4 literals.
|
:return: A list of IPv4 literals.
|
||||||
"""
|
"""
|
||||||
@ -158,15 +177,32 @@ def get_A(host):
|
|||||||
|
|
||||||
# If not using dnspython, attempt lookup using the OS level
|
# If not using dnspython, attempt lookup using the OS level
|
||||||
# getaddrinfo() method.
|
# getaddrinfo() method.
|
||||||
|
if resolver is None or not use_dnspython:
|
||||||
|
try:
|
||||||
|
recs = socket.getaddrinfo(host, None, socket.AF_INET,
|
||||||
|
socket.SOCK_STREAM)
|
||||||
|
return [rec[4][0] for rec in recs]
|
||||||
|
except socket.gaierror:
|
||||||
|
log.debug("DNS: Error retreiving A address info for %s." % host)
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Using dnspython:
|
||||||
try:
|
try:
|
||||||
recs = socket.getaddrinfo(host, None, socket.AF_INET,
|
recs = resolver.query(host, dns.rdatatype.A)
|
||||||
socket.SOCK_STREAM)
|
return [rec.to_text() for rec in recs]
|
||||||
return [rec[4][0] for rec in recs]
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
except socket.gaierror:
|
log.debug("DNS: No A records for %s" % host)
|
||||||
log.debug("DNS: Error retreiving A address info for %s." % host)
|
return []
|
||||||
|
except dns.exception.Timeout:
|
||||||
|
log.debug("DNS: A record resolution timed out for %s" % host)
|
||||||
|
return []
|
||||||
|
except dns.exception.DNSException as e:
|
||||||
|
log.debug("DNS: Error querying A records for %s" % host)
|
||||||
|
log.exception(e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_AAAA(host):
|
|
||||||
|
def get_AAAA(host, resolver=None, use_dnspython=True):
|
||||||
"""Lookup DNS AAAA records for a given host.
|
"""Lookup DNS AAAA records for a given host.
|
||||||
|
|
||||||
If ``resolver`` is not provided, or is ``None``, then resolution will
|
If ``resolver`` is not provided, or is ``None``, then resolution will
|
||||||
@ -174,9 +210,13 @@ def get_AAAA(host):
|
|||||||
|
|
||||||
:param host: The hostname to resolve for AAAA record IPv6 addresses.
|
:param host: The hostname to resolve for AAAA record IPv6 addresses.
|
||||||
:param resolver: Optional DNS resolver object to use for the query.
|
:param resolver: Optional DNS resolver object to use for the query.
|
||||||
|
:param use_dnspython: Optionally control if dnspython is used to make
|
||||||
|
the DNS queries instead of the built-in DNS
|
||||||
|
library.
|
||||||
|
|
||||||
:type host: string
|
:type host: string
|
||||||
:type resolver: :class:`dns.resolver.Resolver` or ``None``
|
:type resolver: :class:`dns.resolver.Resolver` or ``None``
|
||||||
|
:type use_dnspython: bool
|
||||||
|
|
||||||
:return: A list of IPv6 literals.
|
:return: A list of IPv6 literals.
|
||||||
"""
|
"""
|
||||||
@ -184,19 +224,36 @@ def get_AAAA(host):
|
|||||||
|
|
||||||
# If not using dnspython, attempt lookup using the OS level
|
# If not using dnspython, attempt lookup using the OS level
|
||||||
# getaddrinfo() method.
|
# getaddrinfo() method.
|
||||||
if not socket.has_ipv6:
|
if resolver is None or not use_dnspython:
|
||||||
log.debug("Unable to query %s for AAAA records: IPv6 is not supported", host)
|
if not socket.has_ipv6:
|
||||||
return []
|
log.debug("Unable to query %s for AAAA records: IPv6 is not supported", host)
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
recs = socket.getaddrinfo(host, None, socket.AF_INET6,
|
||||||
|
socket.SOCK_STREAM)
|
||||||
|
return [rec[4][0] for rec in recs]
|
||||||
|
except (OSError, socket.gaierror):
|
||||||
|
log.debug("DNS: Error retreiving AAAA address " + \
|
||||||
|
"info for %s." % host)
|
||||||
|
return []
|
||||||
|
|
||||||
|
# Using dnspython:
|
||||||
try:
|
try:
|
||||||
recs = socket.getaddrinfo(host, None, socket.AF_INET6,
|
recs = resolver.query(host, dns.rdatatype.AAAA)
|
||||||
socket.SOCK_STREAM)
|
return [rec.to_text() for rec in recs]
|
||||||
return [rec[4][0] for rec in recs]
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
except (OSError, socket.gaierror):
|
log.debug("DNS: No AAAA records for %s" % host)
|
||||||
log.debug("DNS: Error retreiving AAAA address " + \
|
return []
|
||||||
"info for %s." % host)
|
except dns.exception.Timeout:
|
||||||
|
log.debug("DNS: AAAA record resolution timed out for %s" % host)
|
||||||
|
return []
|
||||||
|
except dns.exception.DNSException as e:
|
||||||
|
log.debug("DNS: Error querying AAAA records for %s" % host)
|
||||||
|
log.exception(e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_SRV(host, port, service, proto='tcp', resolver=None):
|
|
||||||
|
def get_SRV(host, port, service, proto='tcp', resolver=None, use_dnspython=True):
|
||||||
"""Perform SRV record resolution for a given host.
|
"""Perform SRV record resolution for a given host.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
@ -222,7 +279,7 @@ def get_SRV(host, port, service, proto='tcp', resolver=None):
|
|||||||
:return: A list of hostname, port pairs in the order dictacted
|
:return: A list of hostname, port pairs in the order dictacted
|
||||||
by SRV priorities and weights.
|
by SRV priorities and weights.
|
||||||
"""
|
"""
|
||||||
if resolver is None:
|
if resolver is None or not use_dnspython:
|
||||||
log.warning("DNS: dnspython not found. Can not use SRV lookup.")
|
log.warning("DNS: dnspython not found. Can not use SRV lookup.")
|
||||||
return [(host, port)]
|
return [(host, port)]
|
||||||
|
|
||||||
|
@ -224,6 +224,11 @@ class XMLStream(object):
|
|||||||
#: If set to ``True``, attempt to use IPv6.
|
#: If set to ``True``, attempt to use IPv6.
|
||||||
self.use_ipv6 = True
|
self.use_ipv6 = True
|
||||||
|
|
||||||
|
#: If set to ``True``, allow using the ``dnspython`` DNS library
|
||||||
|
#: if available. If set to ``False``, the builtin DNS resolver
|
||||||
|
#: will be used, even if ``dnspython`` is installed.
|
||||||
|
self.use_dnspython = True
|
||||||
|
|
||||||
#: Use CDATA for escaping instead of XML entities. Defaults
|
#: Use CDATA for escaping instead of XML entities. Defaults
|
||||||
#: to ``False``.
|
#: to ``False``.
|
||||||
self.use_cdata = False
|
self.use_cdata = False
|
||||||
@ -1081,7 +1086,8 @@ class XMLStream(object):
|
|||||||
|
|
||||||
return resolve(domain, port, service=self.dns_service,
|
return resolve(domain, port, service=self.dns_service,
|
||||||
resolver=resolver,
|
resolver=resolver,
|
||||||
use_ipv6=self.use_ipv6)
|
use_ipv6=self.use_ipv6,
|
||||||
|
use_dnspython=self.use_dnspython)
|
||||||
|
|
||||||
def pick_dns_answer(self, domain, port=None):
|
def pick_dns_answer(self, domain, port=None):
|
||||||
"""Pick a server and port from DNS answers.
|
"""Pick a server and port from DNS answers.
|
||||||
|
Loading…
Reference in New Issue
Block a user