]> git.ipfire.org Git - thirdparty/dnspython.git/commitdiff
resolve_chaining() now returns a ChainingResult object.
authorBob Halley <halley@dnspython.org>
Mon, 21 Dec 2020 15:39:08 +0000 (07:39 -0800)
committerBob Halley <halley@dnspython.org>
Mon, 21 Dec 2020 15:39:08 +0000 (07:39 -0800)
dns/message.py
dns/resolver.py
doc/message-query.rst
tests/test_message.py

index 5c35dbc737da9b9b67a7a4411a2b3baa4bf05c59..0293e3a2825666a8217230308fa40e60a0b9bd19 100644 (file)
@@ -725,6 +725,31 @@ class Message:
         return (rdclass, rdtype, None, False)
 
 
+class ChainingResult:
+    """The result of a call to dns.message.QueryMessage.resolve_chaining().
+
+    The ``rrset`` attribute is the answer RRSet, or ``None`` if it doesn't
+    exist.
+
+    The ``canonical_name`` attribute is the canonical name after all
+    chaining has been applied (this is the name as ``rrset.name`` in cases
+    where rrset is not ``None``).
+
+    The ``minimum_ttl`` attribute is the minimum TTL, i.e. the TTL to
+    use if caching the data.  It is the smallest of all the CNAME TTLs
+    and either the answer TTL if it exists or the SOA TTL and SOA
+    minimum values for negative answers.
+
+    The ``cnames`` attribute is a list of all the CNAME RRSets followed to
+    get to the canonical name.
+    """
+    def __init__(self, canonical_name, rrset, minimum_ttl, cnames):
+        self.canonical_name = canonical_name
+        self.rrset = rrset
+        self.minimum_ttl = minimum_ttl
+        self.cnames = cnames
+
+
 class QueryMessage(Message):
     def resolve_chaining(self):
         """Follow the CNAME chain in the response to determine the answer
@@ -740,11 +765,7 @@ class QueryMessage(Message):
 
         Raises ``dns.exception.FormError`` if the question count is not 1.
 
-        Returns a tuple (dns.name.Name, int, rrset) where the name is the
-        canonical name, the int is the minimized TTL, and rrset is their
-        answer RRset, which may be ``None`` if the chain was dangling or
-        the response is an NXDOMAIN.
-
+        Returns a ChainingResult object.
         """
         if self.flags & dns.flags.QR == 0:
             raise NotQueryResponse
@@ -755,6 +776,7 @@ class QueryMessage(Message):
         min_ttl = dns.ttl.MAX_TTL
         rrset = None
         count = 0
+        cnames = []
         while count < MAX_CHAIN:
             try:
                 rrset = self.find_rrset(self.answer, qname, question.rdclass,
@@ -767,6 +789,7 @@ class QueryMessage(Message):
                         crrset = self.find_rrset(self.answer, qname,
                                                  question.rdclass,
                                                  dns.rdatatype.CNAME)
+                        cnames.append(crrset)
                         min_ttl = min(min_ttl, crrset.ttl)
                         for rd in crrset:
                             qname = rd.target
@@ -800,7 +823,7 @@ class QueryMessage(Message):
                         auname = auname.parent()
                     except dns.name.NoParent:
                         break
-        return (qname, min_ttl, rrset)
+        return ChainingResult(qname, rrset, min_ttl, cnames)
 
     def canonical_name(self):
         """Return the canonical name of the first name in the question
@@ -816,7 +839,7 @@ class QueryMessage(Message):
 
         Raises ``dns.exception.FormError`` if the question count is not 1.
         """
-        return self.resolve_chaining()[0]
+        return self.resolve_chaining().canonical_name
 
 
 def _maybe_import_update():
index b24f78d54943d74fbfa8c63345c56bb31048207d..d04386fc84bdb0f0fefdffb7635e65c7013b6fe6 100644 (file)
@@ -210,8 +210,12 @@ class Answer:
         self.response = response
         self.nameserver = nameserver
         self.port = port
-        (self.canonical_name, min_ttl, self.rrset) = response.resolve_chaining()
-        self.expiration = time.time() + min_ttl
+        self.chaining_result = response.resolve_chaining()
+        # Copy some attributes out of chaining_result for backwards
+        # compatibilty and convenience.
+        self.canonical_name = self.chaining_result.canonical_name
+        self.rrset = self.chaining_result.rrset
+        self.expiration = time.time() + self.chaining_result.minimum_ttl
 
     def __getattr__(self, attr):  # pragma: no cover
         if attr == 'name':
index 462f7b43a1d12fb884109aa5ba169c462c1663aa..03c5031852174384bbd97299e0915f137079f782 100644 (file)
@@ -3,7 +3,16 @@
 The dns.message.QueryMessage Class
 ----------------------------------
 
-The ``dns.update.QueryMessage`` class is used for ordinary DNS query messages.
+The ``dns.message.QueryMessage`` class is used for ordinary DNS query messages.
 
 .. autoclass:: dns.message.QueryMessage
    :members:
+
+The dns.message.ChainingResult Class
+------------------------------------
+
+Objects of the ``dns.message.ChainingResult`` class are returned by the
+``dns.message.QueryMessage.resolve_chaining()`` method.
+
+.. autoclass:: dns.message.ChainingResult
+   :members:
index ec96458430e75fd62e05a7187aca4ded2a78bfc4..6f62339df5a134a11b030c78929788f7570be019 100644 (file)
@@ -559,10 +559,11 @@ www.example. IN CNAME
 example. 300 IN SOA . . 1 2 3 4 5
 ''')
         # passing is actuall not going into an infinite loop in this call
-        (qname, min_ttl, rrset) = r.resolve_chaining()
-        self.assertEqual(qname, dns.name.from_text('www.example.'))
-        self.assertEqual(min_ttl, 5)
-        self.assertIsNone(rrset)
+        result = r.resolve_chaining()
+        self.assertEqual(result.canonical_name,
+                         dns.name.from_text('www.example.'))
+        self.assertEqual(result.minimum_ttl, 5)
+        self.assertIsNone(result.rrset)
 
     def test_bad_text_questions(self):
         with self.assertRaises(dns.exception.SyntaxError):