]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
Add canonical_name() method to resolver.
authorBob Halley <halley@dnspython.org>
Sat, 18 Jul 2020 14:00:28 +0000 (07:00 -0700)
committerBob Halley <halley@dnspython.org>
Mon, 20 Jul 2020 13:18:43 +0000 (06:18 -0700)
dns/resolver.py
doc/resolver-functions.rst
tests/test_resolver.py

index ec163761bfffcce5c3a7f88c20f86f033dfdfd6a..b259b907d1749d11e238266123c5c26c95bf15c8 100644 (file)
@@ -1172,6 +1172,29 @@ class Resolver:
                             rdclass=dns.rdataclass.IN,
                             *args, **kwargs)
 
+    def canonical_name(self, name):
+        """Determine the canonical name of *name*.
+
+        The canonical name is the name the resolver uses for queries
+        after all CNAME and DNAME renamings have been applied.
+
+        *name*, a ``dns.name.Name`` or ``str``, the query name.
+
+        This method can raise any exception that ``resolve()`` can
+        raise, other than `dns.resolver.NoAnswer`` and
+        ``dns.resolver.NXDOMAIN``.
+
+        Returns a ``dns.name.Name``.
+        """
+        if isinstance(name, str):
+            name = dns.name.from_text(name)
+        try:
+            answer = self.resolve(name, raise_on_no_answer=False)
+            canonical_name = answer.canonical_name
+        except dns.resolver.NXDOMAIN as e:
+            canonical_name = e.canonical_name
+        return canonical_name
+
     def use_tsig(self, keyring, keyname=None,
                  algorithm=dns.tsig.default_algorithm):
         """Add a TSIG signature to each query.
@@ -1296,6 +1319,16 @@ def resolve_address(ipaddr, *args, **kwargs):
     return get_default_resolver().resolve_address(ipaddr, *args, **kwargs)
 
 
+def canonical_name(name):
+    """Determine the canonical name of *name*.
+
+    See ``dns.resolver.Resolver.canonical_name`` for more information on the
+    parameters and possible exceptions.
+    """
+
+    return get_default_resolver().canonical_name(name)
+
+
 def zone_for_name(name, rdclass=dns.rdataclass.IN, tcp=False, resolver=None):
     """Find the name of the zone which contains the specified name.
 
index 6e579571b00c6637705eb031ce5a687fbad7f7b1..179484cd3e994a3005eb4a756cb78868f65c52d1 100644 (file)
@@ -5,6 +5,7 @@ Resolver Functions and The Default Resolver
 
 .. autofunction:: dns.resolver.resolve
 .. autofunction:: dns.resolver.resolve_address
+.. autofunction:: dns.resolver.canonical_name
 .. autofunction:: dns.resolver.zone_for_name
 .. autofunction:: dns.resolver.query
 .. autodata:: dns.resolver.default_resolver
index b63ec1963ccb5c81f4feaa531f52902cc5435208..915a3d1720c8bf5375a2e7de501c441dd84c6a16 100644 (file)
@@ -510,6 +510,20 @@ class LiveResolverTests(unittest.TestCase):
         answer2 = res.resolve('dns.google.', 'A')
         self.assertIs(answer2, answer1)
 
+    def testCanonicalNameNoCNAME(self):
+        cname = dns.name.from_text('www.google.com')
+        self.assertTrue(dns.resolver.canonical_name('www.google.com') == cname)
+
+    def testCanonicalNameCNAME(self):
+        name = dns.name.from_text('www.dnspython.org')
+        cname = dns.name.from_text('dmfrjf4ips8xa.cloudfront.net')
+        self.assertTrue(dns.resolver.canonical_name(name) == cname)
+
+    def testCanonicalNameDangling(self):
+        name = dns.name.from_text('dangling-cname.dnspython.org')
+        cname = dns.name.from_text('dangling-target.dnspython.org')
+        self.assertTrue(dns.resolver.canonical_name(name) == cname)
+
 class PollingMonkeyPatchMixin(object):
     def setUp(self):
         self.__native_selector_class = dns.query._selector_class