]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Add resolver resolve(), deprecate query().
authorBob Halley <halley@dnspython.org>
Fri, 15 May 2020 12:50:10 +0000 (05:50 -0700)
committerBob Halley <halley@dnspython.org>
Fri, 15 May 2020 12:50:10 +0000 (05:50 -0700)
dns/e164.py
dns/resolver.py
dns/resolver.pyi
doc/resolver-functions.rst
tests/test_nsec3_hash.py
tests/test_resolver.py

index d6e695b0c8be946f38ff0d6e2e8a07a951990aa9..83731b2c56260b79b35fe46634f04c7e339fe51d 100644 (file)
@@ -98,7 +98,7 @@ def query(number, domains, resolver=None):
             domain = dns.name.from_text(domain)
         qname = dns.e164.from_e164(number, domain)
         try:
-            return resolver.query(qname, 'NAPTR')
+            return resolver.resolve(qname, 'NAPTR')
         except dns.resolver.NXDOMAIN as e:
             e_nx += e
     raise e_nx
index ee4a1913bbc50142372735fd8d21d89c72b90104..3c250b0c216434cd5b75b64bf764461af1582e7e 100644 (file)
@@ -22,6 +22,7 @@ import socket
 import sys
 import time
 import random
+import warnings
 try:
     import threading as _threading
 except ImportError:
@@ -805,9 +806,26 @@ class Resolver(object):
             raise Timeout(timeout=duration)
         return min(lifetime - duration, self.timeout)
 
-    def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
-              tcp=False, source=None, raise_on_no_answer=True, source_port=0,
-              lifetime=None):
+    def _get_qnames_to_try(self, qname, search):
+        # This is a separate method so we can unit test the search
+        # rules without requiring the Internet.
+        qnames_to_try = []
+        if qname.is_absolute():
+            qnames_to_try.append(qname)
+        else:
+            if len(qname) > 1:
+                qnames_to_try.append(qname.concatenate(dns.name.root))
+            if search and self.search:
+                for suffix in self.search:
+                    if self.ndots is None or len(qname.labels) >= self.ndots:
+                        qnames_to_try.append(qname.concatenate(suffix))
+            else:
+                qnames_to_try.append(qname.concatenate(self.domain))
+        return qnames_to_try
+
+    def resolve(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+                tcp=False, source=None, raise_on_no_answer=True, source_port=0,
+                lifetime=None, search=False):
         """Query nameservers to find the answer to the question.
 
         The *qname*, *rdtype*, and *rdclass* parameters may be objects
@@ -830,7 +848,12 @@ class Resolver(object):
 
         *source_port*, an ``int``, the port from which to send the message.
 
-        *lifetime*, a ``float``, how many seconds a query should run before timing out.
+        *lifetime*, a ``float``, how many seconds a query should run
+         before timing out.
+
+        *search*, a ``bool``, determines whether search lists configured
+        in the system's resolver configuration are used.  The default is
+        ``False``.
 
         Raises ``dns.exception.Timeout`` if no answers could be found
         in the specified lifetime.
@@ -848,6 +871,7 @@ class Resolver(object):
         nameservers are available to answer the question.
 
         Returns a ``dns.resolver.Answer`` instance.
+
         """
 
         if isinstance(qname, str):
@@ -860,18 +884,7 @@ class Resolver(object):
             rdclass = dns.rdataclass.from_text(rdclass)
         if dns.rdataclass.is_metaclass(rdclass):
             raise NoMetaqueries
-        qnames_to_try = []
-        if qname.is_absolute():
-            qnames_to_try.append(qname)
-        else:
-            if len(qname) > 1:
-                qnames_to_try.append(qname.concatenate(dns.name.root))
-            if self.search:
-                for suffix in self.search:
-                    if self.ndots is None or len(qname.labels) >= self.ndots:
-                        qnames_to_try.append(qname.concatenate(suffix))
-            else:
-                qnames_to_try.append(qname.concatenate(self.domain))
+        qnames_to_try = self._get_qnames_to_try(qname, search)
         all_nxdomain = True
         nxdomain_responses = {}
         start = time.time()
@@ -1035,22 +1048,40 @@ class Resolver(object):
             self.cache.put((_qname, rdtype, rdclass), answer)
         return answer
 
-    def reverse_query(self, ipaddr, *args, **kwargs):
-        """Use a resolver to run a Reverse IP Query for PTR records.
+    def query(self, qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+              tcp=False, source=None, raise_on_no_answer=True, source_port=0,
+              lifetime=None):
+        """Query nameservers to find the answer to the question.
+
+        This method calls resolve() with ``search=True``, and is
+        provided for backwards compatbility with prior versions of
+        dnspython.  See the documentation for the resolve() method for
+        further details.
+        """
+        warnings.warn('please use dns.resolver.Resolver.resolve() instead',
+                      DeprecationWarning, stacklevel=2)
+        return self.resolve(qname, rdtype, rdclass, tcp, source,
+                            raise_on_no_answer, source_port, lifetime,
+                            True)
+
+    def resolve_address(self, ipaddr, *args, **kwargs):
+        """Use a resolver to run a reverse query for PTR records.
 
-        This utilizes the in-built query function to perform a PTR lookup on the
+        This utilizes the resolve() method to perform a PTR lookup on the
         specified IP address.
 
-        *ipaddr*, a ``str``, the IP address you want to get the PTR record for.
+        *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get
+        the PTR record for.
 
-        All other arguments that can be passed to the query function except for
-        rdtype and rdclass are also supported by this function.
+        All other arguments that can be passed to the resolve() function
+        except for rdtype and rdclass are also supported by this
+        function.
         """
 
-        return self.query(dns.reversename.from_address(ipaddr),
-                          rdtype=dns.rdatatype.PTR,
-                          rdclass=dns.rdataclass.IN,
-                          *args, **kwargs)
+        return self.resolve(dns.reversename.from_address(ipaddr),
+                            rdtype=dns.rdatatype.PTR,
+                            rdclass=dns.rdataclass.IN,
+                            *args, **kwargs)
 
     def use_tsig(self, keyring, keyname=None,
                  algorithm=dns.tsig.default_algorithm):
@@ -1148,21 +1179,37 @@ def reset_default_resolver():
     default_resolver = Resolver()
 
 
-def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
-          tcp=False, source=None, raise_on_no_answer=True,
-          source_port=0, lifetime=None):
+def resolve(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+            tcp=False, source=None, raise_on_no_answer=True,
+            source_port=0, lifetime=None, search=False):
     """Query nameservers to find the answer to the question.
 
     This is a convenience function that uses the default resolver
     object to make the query.
 
-    See ``dns.resolver.Resolver.query`` for more information on the
+    See ``dns.resolver.Resolver.resolve`` for more information on the
     parameters.
     """
 
-    return get_default_resolver().query(qname, rdtype, rdclass, tcp, source,
-                                        raise_on_no_answer, source_port,
-                                        lifetime)
+    return get_default_resolver().resolve(qname, rdtype, rdclass, tcp, source,
+                                          raise_on_no_answer, source_port,
+                                          lifetime, search)
+
+def query(qname, rdtype=dns.rdatatype.A, rdclass=dns.rdataclass.IN,
+          tcp=False, source=None, raise_on_no_answer=True,
+          source_port=0, lifetime=None):
+    """Query nameservers to find the answer to the question.
+
+    This method calls resolve() with ``search=True``, and is
+    provided for backwards compatbility with prior versions of
+    dnspython.  See the documentation for the resolve() method for
+    further details.
+    """
+    warnings.warn('please use dns.resolver.resolve() instead',
+                  DeprecationWarning, stacklevel=2)
+    return resolve(qname, rdtype, rdclass, tcp, source,
+                   raise_on_no_answer, source_port, lifetime,
+                   True)
 
 
 def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
@@ -1192,7 +1239,7 @@ def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
         raise NotAbsolute(name)
     while 1:
         try:
-            answer = resolver.query(name, dns.rdatatype.SOA, rdclass, tcp)
+            answer = resolver.resolve(name, dns.rdatatype.SOA, rdclass, tcp)
             if answer.rrset.name == name:
                 return name
             # otherwise we were CNAMEd or DNAMEd and need to look higher
index 39f1e65651d824459101dc18aee4faf87a547c57..3fb2931a79b2caa9951b982685c667bf5e6ba1a6 100644 (file)
@@ -14,11 +14,18 @@ class NoMetaqueries(exception.DNSException): ...
 class NoResolverConfiguration(exception.DNSException): ...
 Timeout = exception.Timeout
 
-def query(qname : str, rdtype : Union[int,str] = 0, rdclass : Union[int,str] = 0,
+def resolve(qname : str, rdtype : Union[int,str] = 0,
+            rdclass : Union[int,str] = 0,
+            tcp=False, source=None, raise_on_no_answer=True,
+            source_port=0, lifetime : Optional[float]=None,
+            search : bool = False):
+    ...
+def query(qname : str, rdtype : Union[int,str] = 0,
+          rdclass : Union[int,str] = 0,
           tcp=False, source=None, raise_on_no_answer=True,
-          source_port=0):
+          source_port=0, lifetime : Optional[float]=None):
     ...
-def reverse_query(self, ipaddr: str, *args: Any, **kwargs: Optional[Dict]):
+def resolve_address(self, ipaddr: str, *args: Any, **kwargs: Optional[Dict]):
     ...
 class LRUCache:
     def __init__(self, max_size=1000):
@@ -31,12 +38,23 @@ class Answer:
     def __init__(self, qname, rdtype, rdclass, response,
                  raise_on_no_answer=True):
         ...
-def zone_for_name(name, rdclass : int = rdataclass.IN, tcp=False, resolver : Optional[Resolver] = None):
+def zone_for_name(name, rdclass : int = rdataclass.IN, tcp=False,
+                  resolver : Optional[Resolver] = None):
     ...
 
 class Resolver:
-    def __init__(self, filename : Optional[str] = '/etc/resolv.conf', configure : Optional[bool] = True):
+    def __init__(self, filename : Optional[str] = '/etc/resolv.conf',
+                 configure : Optional[bool] = True):
         self.nameservers : List[str]
-    def query(self, qname : str, rdtype : Union[int,str] = rdatatype.A, rdclass : Union[int,str] = rdataclass.IN,
-              tcp : bool = False, source : Optional[str] = None, raise_on_no_answer=True, source_port : int = 0):
+    def resolve(self, qname : str, rdtype : Union[int,str] = rdatatype.A,
+                rdclass : Union[int,str] = rdataclass.IN,
+                tcp : bool = False, source : Optional[str] = None,
+                raise_on_no_answer=True, source_port : int = 0,
+                lifetime : Optional[float]=None, search : bool = False):
+        ...
+    def query(self, qname : str, rdtype : Union[int,str] = rdatatype.A,
+              rdclass : Union[int,str] = rdataclass.IN,
+              tcp : bool = False, source : Optional[str] = None,
+              raise_on_no_answer=True, source_port : int = 0,
+              lifetime : Optional[float]=None):
         ...
index d3c5dccc6b7ca4f7c10c4f70594c76d74add7efa..1fb432c6a5de22dd777bc71793fdabf0b88fea73 100644 (file)
@@ -3,6 +3,7 @@
 Resolver Functions and The Default Resolver
 ===========================================
 
+.. autofunction:: dns.resolver.resolve
 .. autofunction:: dns.resolver.query
 .. autofunction:: dns.resolver.zone_for_name
 .. autodata:: dns.resolver.default_resolver
index 58bdeb36d71ddab310b6fd1f9b6b213927934639..6f18240def75332acff25ed26864bda7ea58fa3a 100644 (file)
@@ -1,6 +1,6 @@
 import unittest
 
-from dns import dnssec,name
+from dns import dnssec, name
 
 
 class NSEC3Hash(unittest.TestCase):
index 580965083b9ce193735fac2b34c5a1a981f9aec0..f6ad7624f9eaa51d5ff45d51719ff0428391b9f1 100644 (file)
@@ -264,6 +264,25 @@ class BaseResolverTests(unittest.TestCase):
         for a in answer:
             pass
 
+    def testSearchLists(self):
+        res = dns.resolver.Resolver()
+        res.domain = dns.name.from_text('example')
+        res.search = [dns.name.from_text(x) for x in
+                      ['dnspython.org', 'dnspython.net']]
+        qname = dns.name.from_text('www', None)
+        qnames = res._get_qnames_to_try(qname, True)
+        self.assertEqual(qnames,
+                         [dns.name.from_text(x) for x in
+                          ['www.dnspython.org', 'www.dnspython.net']])
+        qnames = res._get_qnames_to_try(qname, False)
+        self.assertEqual(qnames,
+                         [dns.name.from_text('www.example.')])
+        qname = dns.name.from_text('absolute')
+        qnames = res._get_qnames_to_try(qname, True)
+        self.assertEqual(qnames, [qname])
+        qnames = res._get_qnames_to_try(qname, False)
+        self.assertEqual(qnames, [qname])
+
 class PollingMonkeyPatchMixin(object):
     def setUp(self):
         self.__native_polling_backend = dns.query._polling_backend