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
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
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,
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
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
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():
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':
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:
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):