dnspython: Add compatibility shim

`dnspython` 2.0.0 has many changes and several deprecations like:

```
> dns.resolver.resolve() has been added, allowing control of whether
search lists are used. dns.resolver.query() is retained for backwards
compatibility, but deprecated. The default for search list behavior can
be set at in the resolver object with the use_search_by_default
parameter. The default is False.

> dns.resolver.resolve_address() has been added, allowing easy
address-to-name lookups.
```

The new class `DNSResolver`:
- provides the compatibility layer
- defaults the previous behavior (the search list configured in the
  system's resolver configuration is used for relative names)
- defaults lifetime to 15sec (determines the number of seconds
  to spend trying to get an answer to the question)

Fixes: https://pagure.io/freeipa/issue/8383
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Stanislav Levin
2020-08-28 16:31:10 +03:00
committed by Alexander Bokovoy
parent fdb227e55a
commit 49e643783d
16 changed files with 158 additions and 86 deletions

View File

@@ -27,6 +27,7 @@ import dns.exception
import dns.resolver
import dns.rdataclass
import dns.rdatatype
import dns.reversename
import six
@@ -39,6 +40,85 @@ if six.PY3:
logger = logging.getLogger(__name__)
ipa_resolver = None
def get_ipa_resolver():
global ipa_resolver
if ipa_resolver is None:
ipa_resolver = DNSResolver()
return ipa_resolver
def resolve(*args, **kwargs):
return get_ipa_resolver().resolve(*args, **kwargs)
def resolve_address(*args, **kwargs):
return get_ipa_resolver().resolve_address(*args, **kwargs)
def zone_for_name(*args, **kwargs):
if "resolver" not in kwargs:
kwargs["resolver"] = get_ipa_resolver()
return dns.resolver.zone_for_name(*args, **kwargs)
def reset_default_resolver():
"""Re-initialize ipa resolver.
"""
global ipa_resolver
ipa_resolver = DNSResolver()
class DNSResolver(dns.resolver.Resolver):
"""DNS stub resolver compatible with both dnspython < 2.0.0
and dnspython >= 2.0.0.
Set `use_search_by_default` attribute to `True`, which
determines the default for whether the search list configured
in the system's resolver configuration is used for relative
names, and whether the resolver's domain may be added to relative
names.
Increase the default lifetime which determines the number of seconds
to spend trying to get an answer to the question. dnspython 2.0.0
changes this to 5sec, while the previous one was 30sec.
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.reset_ipa_defaults()
self.resolve = getattr(super(), "resolve", self.query)
self.resolve_address = getattr(
super(),
"resolve_address",
self._resolve_address
)
def reset_ipa_defaults(self):
self.use_search_by_default = True
# the default is 5sec
self.lifetime = 15
def reset(self):
super().reset()
self.reset_ipa_defaults()
def _resolve_address(self, ip_address, *args, **kwargs):
"""Query nameservers for PTR records.
:param ip_address: IPv4 or IPv6 address
:type ip_address: str
"""
return resolve(
dns.reversename.from_address(ip_address),
rdtype=dns.rdatatype.PTR,
*args,
**kwargs,
)
class DNSZoneAlreadyExists(dns.exception.DNSException):
supp_kwargs = {'zone', 'ns'}
fmt = (u"DNS zone {zone} already exists in DNS "
@@ -321,7 +401,7 @@ def resolve_rrsets(fqdn, rdtypes):
rrsets = []
for rdtype in rdtypes:
try:
answer = dns.resolver.query(fqdn, rdtype)
answer = resolve(fqdn, rdtype)
logger.debug('found %d %s records for %s: %s',
len(answer),
rdtype,
@@ -363,7 +443,7 @@ def check_zone_overlap(zone, raise_on_error=True):
return
try:
containing_zone = dns.resolver.zone_for_name(zone)
containing_zone = zone_for_name(zone)
except dns.exception.DNSException as e:
msg = ("DNS check for domain %s failed: %s." % (zone, e))
if raise_on_error:
@@ -374,7 +454,7 @@ def check_zone_overlap(zone, raise_on_error=True):
if containing_zone == zone:
try:
ns = [ans.to_text() for ans in dns.resolver.query(zone, 'NS')]
ns = [ans.to_text() for ans in resolve(zone, 'NS')]
except dns.exception.DNSException as e:
logger.debug("Failed to resolve nameserver(s) for domain %s: %s",
zone, e)
@@ -463,6 +543,8 @@ def query_srv(qname, resolver=None, **kwargs):
:return: list of dns.rdtypes.IN.SRV.SRV instances
"""
if resolver is None:
resolver = dns.resolver
answer = resolver.query(qname, rdtype=dns.rdatatype.SRV, **kwargs)
resolve_f = resolve
else:
resolve_f = getattr(resolver, "resolve", resolver.query)
answer = resolve_f(qname, rdtype=dns.rdatatype.SRV, **kwargs)
return sort_prio_weight(answer)

View File

@@ -43,9 +43,6 @@ import locale
import collections
import urllib
from dns import resolver, reversename
from dns.exception import DNSException
import six
from six.moves import input
@@ -1112,22 +1109,6 @@ def check_port_bindable(port, socket_type=socket.SOCK_STREAM):
s.close()
def reverse_record_exists(ip_address):
"""
Checks if IP address have some reverse record somewhere.
Does not care where it points.
Returns True/False
"""
reverse = reversename.from_address(str(ip_address))
try:
resolver.query(reverse, "PTR")
except DNSException:
# really don't care what exception, PTR is simply unresolvable
return False
return True
def config_replace_variables(filepath, replacevars=dict(), appendvars=dict(),
removevars=None):
"""