Home | History | Annotate | Line # | Download | only in additional
      1 # Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      2 #
      3 # SPDX-License-Identifier: MPL-2.0
      4 #
      5 # This Source Code Form is subject to the terms of the Mozilla Public
      6 # License, v. 2.0.  If a copy of the MPL was not distributed with this
      7 # file, you can obtain one at https://mozilla.org/MPL/2.0/.
      8 #
      9 # See the COPYRIGHT file distributed with this work for additional
     10 # information regarding copyright ownership.
     11 
     12 import dns.message
     13 import dns.name
     14 import dns.rdatatype
     15 
     16 import isctest
     17 
     18 
     19 def _query(ns, qname, qtype):
     20     msg = isctest.query.create(qname, qtype)
     21     res = isctest.query.udp(msg, ns.ip)
     22     isctest.check.noerror(res)
     23     return res
     24 
     25 
     26 def _additional(res, target):
     27     """
     28     Return the additional RRsets of 'res' keyed by type, checking that
     29     all of them are owned by 'target'.
     30     """
     31     owner = dns.name.from_text(target)
     32     assert all(rrset.name == owner for rrset in res.additional), res.additional
     33     return {rrset.rdtype: rrset for rrset in res.additional}
     34 
     35 
     36 def test_https_alias_target_too_many_records(ns3):
     37     """
     38     Resolve HTTPS AliasMode records whose targets are already cached, then
     39     shut named down and check that nothing was leaked.
     40 
     41     The target of alias14 is a 14-record ServiceMode RRset, i.e. more than
     42     DNS_RDATASET_MAXADDITIONAL.  Following the alias clones the cached
     43     target RRset into the caller's rdataset, and the subsequent additional
     44     processing of that RRset fails with DNS_R_TOOMANYRECORDS.  That error
     45     used to be returned before the clone was disassociated, leaking a
     46     reference to the cache node and its slab for every such query.
     47 
     48     The target of alias13 is at the limit and is processed normally.
     49 
     50     The limit bounds the processing of a target RRset for its own
     51     additional data, not the inclusion of the target RRset itself, which
     52     is added to the response before it is processed.  The ServiceMode
     53     records point at "." (the owner name), so processing a target RRset
     54     adds the target's cached A record: it is present for target13 and
     55     absent for target14.
     56     """
     57     # Prime the cache with both target RRsets and their A records.
     58     res = _query(ns3, "target14.https.example.", "HTTPS")
     59     isctest.check.rr_count_eq(res.answer, 14)
     60     res = _query(ns3, "target13.https.example.", "HTTPS")
     61     isctest.check.rr_count_eq(res.answer, 13)
     62     for target in ("target14", "target13"):
     63         res = _query(ns3, f"{target}.https.example.", "A")
     64         isctest.check.rr_count_eq(res.answer, 1)
     65 
     66     # The first pass resolves the aliases recursively, the second one is
     67     # answered from the cache.
     68     for _ in range(2):
     69         # An error while collecting the additional data must not turn into
     70         # a failed response (RFC 9460 section 4.2).
     71         res = _query(ns3, "alias14.https.example.", "HTTPS")
     72         expected = dns.message.from_text(""";ANSWER
     73 alias14.https.example. 86400 IN HTTPS 0 target14.https.example.
     74 """)
     75         isctest.check.rrsets_equal(res.answer, expected.answer)
     76         # The target RRset is returned in full, but as it is over the
     77         # limit it is not processed, so its A record is not added.
     78         target14 = _additional(res, "target14.https.example.")
     79         assert set(target14) == {dns.rdatatype.HTTPS}, res.additional
     80         isctest.check.rr_count_eq([target14[dns.rdatatype.HTTPS]], 14)
     81 
     82         # The alias at the limit is followed, its target is included, and
     83         # the target is processed in turn, adding its A record.
     84         res = _query(ns3, "alias13.https.example.", "HTTPS")
     85         expected = dns.message.from_text(""";ANSWER
     86 alias13.https.example. 86400 IN HTTPS 0 target13.https.example.
     87 """)
     88         isctest.check.rrsets_equal(res.answer, expected.answer)
     89         target13 = _additional(res, "target13.https.example.")
     90         assert set(target13) == {dns.rdatatype.HTTPS, dns.rdatatype.A}, res.additional
     91         isctest.check.rr_count_eq([target13[dns.rdatatype.HTTPS]], 13)
     92         expected = dns.message.from_text(""";ADDITIONAL
     93 target13.https.example. 86400 IN A 10.53.0.13
     94 """)
     95         isctest.check.rrsets_equal([target13[dns.rdatatype.A]], expected.additional)
     96 
     97     # Stop the server and check for leaked references.  A leaked cache
     98     # rdataset pins the cache database and its memory context, which shows
     99     # up in named's memory tracking output at exit.
    100     ns3.stop()
    101     assert "outstanding memory" not in ns3.log
    102