Commit 77402c00 authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'minimal-responses' into 'master'

minimal responses

See merge request !396
parents b09365a2 c998e6b3
......@@ -218,35 +218,6 @@ static int put_answer(knot_pkt_t *pkt, uint16_t type, struct query_data *qdata)
return ret;
}
/*! \brief Puts optional NS RRSet to the Authority section of the response. */
static int put_authority_ns(knot_pkt_t *pkt, struct query_data *qdata)
{
const zone_contents_t *zone = qdata->zone->contents;
dbg_ns("%s(%p, %p)\n", __func__, pkt, qdata);
/* DS/DNSKEY queries are not referrals. NS is optional.
* But taking response size into consideration, DS/DNSKEY RRs
* are rather large and may trigger fragmentation or even TCP
* recovery. */
uint16_t query_type = knot_pkt_qtype(pkt);
if (query_type == KNOT_RRTYPE_DS || /* Too large response */
query_type == KNOT_RRTYPE_DNSKEY || /* Too large response */
qdata->node == NULL /* CNAME leading to non-existent name.*/ ) {
dbg_ns("%s: not adding AUTHORITY NS for this response\n", __func__);
return KNOT_EOK;
}
knot_rrset_t ns_rrset = node_rrset(zone->apex, KNOT_RRTYPE_NS);
if (!knot_rrset_empty(&ns_rrset)) {
knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
return ns_put_rr(pkt, &ns_rrset, &rrsigs, KNOT_COMPR_HINT_NONE,
KNOT_PF_NOTRUNC|KNOT_PF_CHECKDUP, qdata);
} else {
dbg_ns("%s: no NS RRSets in this zone, fishy...\n", __func__);
}
return KNOT_EOK;
}
/*! \brief Puts optional SOA RRSet to the Authority section of the response. */
static int put_authority_soa(knot_pkt_t *pkt, struct query_data *qdata,
const zone_contents_t *zone)
......@@ -579,9 +550,9 @@ static int solve_authority(int state, knot_pkt_t *pkt, struct query_data *qdata,
const zone_contents_t *zone_contents = qdata->zone->contents;
switch (state) {
case HIT: /* Positive response, add (optional) AUTHORITY NS. */
case HIT: /* Positive response. */
dbg_ns("%s: answer is POSITIVE\n", __func__);
ret = put_authority_ns(pkt, qdata);
ret = KNOT_EOK;
break;
case MISS: /* MISS, set NXDOMAIN RCODE. */
dbg_ns("%s: answer is NXDOMAIN\n", __func__);
......
......@@ -27,6 +27,9 @@ cname CNAME dns1
; CNAME to delegation
cname-ns CNAME sub
; CNAME to a name below delegation
cname-below-ns CNAME below.sub
; CNAME to out
cname-out CNAME another.world.
......
$ORIGIN test.
$TTL 10
@ SOA a.ns admin 1 10 10 120 10
; NS - autoritative
@ NS a.ns
NS b.ns
NS ns.out.
a.ns A 10.0.0.1
AAAA fd00::a1
b.ns AAAA fd00::b1
AAAA fd00::b2
; NS - delegation
deleg NS a.ns.deleg
NS ns.out.
a.ns.deleg A 10.0.1.1
AAAA fd00::1:1
; MX
mx MX 10 a.mail
MX 20 b.mail
MX 30 mail.out.
a.mail A 10.0.1.1
b.mail AAAA fd00::1:1
; SRV
srv SRV 10 0 1000 a.service
SRV 10 0 1000 b.service
SRV 10 0 1000 service.out.
a.service TXT "no address"
b.service AAAA fd00::2:1
AAAA fd00::2:2
AAAA fd00::2:3
; PTR
ptr PTR target
target A 10.0.3.1
AAAA fd00::3:1
; MX with CNAME
mx-cname MX 10 cname.mail
cname.mail CNAME a.mail
#!/usr/bin/env python3
"""
Basic checks of Additional section content.
- Query into a delegation scope adds glue into additionals.
- Query for NS/MX/SRV adds target A+AAAA into additionals.
- Query for other types (like PTR) does NOT cause lookup of additionals.
- Query for NS/MX/SRV pointing to CNAME does NOT cause lookup of additionals.
"""
from dnstest.test import Test
t = Test()
knot = t.server("knot")
zone = t.zone("test", storage=".")
t.link(zone, knot)
t.start()
# NS authoritative
resp = knot.dig("test", "NS")
resp.check(rcode="NOERROR", flags="AA")
resp.check_rr("answer", "test", "NS")
resp.check_rr("additional", "a.ns.test", "A")
resp.check_rr("additional", "a.ns.test", "AAAA")
resp.check_rr("additional", "b.ns.test", "AAAA")
# NS delegation
resp = knot.dig("www.deleg.test", "A")
resp.check(rcode="NOERROR", noflags="AA")
resp.check_empty(section="answer")
resp.check_rr("authority", "deleg.test", "NS")
resp.check_rr("additional", "a.ns.deleg.test", "A")
resp.check_rr("additional", "a.ns.deleg.test", "AAAA")
# MX record
resp = knot.dig("mx.test", "MX")
resp.check(rcode="NOERROR", flags="AA")
resp.check_rr("answer", "mx.test", "MX")
resp.check_rr("additional", "a.mail.test", "A")
resp.check_rr("additional", "b.mail.test", "AAAA")
# SRV record (only AAAA in additionals)
resp = knot.dig("srv.test", "SRV")
resp.check(rcode="NOERROR", flags="AA")
resp.check_rr("answer", "srv.test", "SRV")
resp.check_rr("additional", "b.service.test", "AAAA")
# PTR record (no additionals expected)
resp = knot.dig("ptr.test", "PTR")
resp.check(rcode="NOERROR", flags="AA")
resp.check_rr("answer", "ptr.test", "PTR")
resp.check_empty("additional")
# MX through CNAME (no additionals expected)
resp = knot.dig("mx-cname.test", "MX")
resp.check(rcode="NOERROR", flags="AA")
resp.check_rr("answer", "mx-cname.test", "MX")
resp.check_empty("additional")
t.stop()
......@@ -41,7 +41,7 @@ resp.cmp(bind)
# AA flag must be cleared
resp = knot.dig("sub.flags", "NS", flags="AA")
resp.check(flags="QR", noflags="AA TC RD RA AD CD")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# RA flag must be cleared
resp = knot.dig("flags", "NS", flags="RA")
......@@ -51,7 +51,7 @@ resp.cmp(bind)
# NS record for delegated subdomain (not authoritative).
resp = knot.dig("sub.flags", "NS")
resp.check(flags="QR", noflags="AA TC RD RA AD CD")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Glue record for delegated subdomain (not authoritative).
resp = knot.dig("ns.sub.flags", "A")
......@@ -61,12 +61,12 @@ resp.cmp(bind)
# Check maximal UDP payload which fits into a response message.
resp = knot.dig("512resp.flags", "TXT", udp=True)
resp.check(flags="QR AA", noflags="TC RD RA AD CD")
resp.cmp(bind, flags=False) # Bind returns TC compared to Knot!
resp.cmp(bind)
# TC bit - UDP.
resp = knot.dig("513resp.flags", "TXT", udp=True)
resp.check(flags="QR AA TC", noflags="RD RA AD CD")
resp.cmp(bind, authority=False) # Knot puts SOA compared to Bind!
resp.cmp(bind)
# No TC bit - TCP.
resp = knot.dig("513resp.flags", "TXT", udp=False)
......@@ -76,9 +76,11 @@ resp.cmp(bind)
# Check ANY over UDP (expects TC=1)
resp = knot.dig("flags", "ANY", udp=True)
resp.check(flags="QR AA TC", noflags="RD RA AD CD")
# nothing to compare
# Check ANY over TCP(expects TC=0)
# Check ANY over TCP (expects TC=0)
resp = knot.dig("flags", "ANY", udp=False)
resp.check(flags="QR AA", noflags="TC RD RA AD CD")
resp.cmp(bind)
t.end()
......@@ -34,12 +34,12 @@ resp.cmp(bind)
# B4. Referral to Signed Zone.
resp = knot.dig("mc.a.example", "MX", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B5. Referral to Unsigned Zone.
resp = knot.dig("mc.b.example", "MX", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B6. Wildcard Expansion.
resp = knot.dig("a.z.w.example", "MX", dnssec=True)
......@@ -69,12 +69,12 @@ resp.cmp(bind)
# Wildcard Expansion below delegation point
resp = knot.dig("a.a.example", "A", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Wildcard Expansion below delegation point (no data)
resp = knot.dig("a.a.example", "AAAA", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Direct wildcard query (positive)
resp = knot.dig("*.w.example", "MX", dnssec=True)
......@@ -89,12 +89,12 @@ resp.cmp(bind)
# Direct wildcard query below delegation point (positive)
resp = knot.dig("*.a.example", "A", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Direct wildcard query below delegation point (no data)
resp = knot.dig("*.a.example", "AAAA", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B8. DS Child Zone No Data Error.
resp = knot.dig("example", "DS", dnssec=True)
......
......@@ -34,12 +34,12 @@ resp.cmp(bind)
# B4. Referral to Signed Zone.
resp = knot.dig("mc.a.example", "MX", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B5. Referral to Unsigned Zone.
resp = knot.dig("mc.b.example", "MX", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B6. Wildcard Expansion.
resp = knot.dig("a.z.w.example", "MX", dnssec=True)
......@@ -69,12 +69,12 @@ resp.cmp(bind)
# Wildcard Expansion below delegation point
resp = knot.dig("a.a.example", "A", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Wildcard Expansion below delegation point (no data)
resp = knot.dig("a.a.example", "AAAA", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Direct wildcard query (positive)
resp = knot.dig("*.w.example", "MX", dnssec=True)
......@@ -89,12 +89,12 @@ resp.cmp(bind)
# Direct wildcard query below delegation point (positive)
resp = knot.dig("*.a.example", "A", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Direct wildcard query below delegation point (no data)
resp = knot.dig("*.a.example", "AAAA", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B8. DS Child Zone No Data Error.
resp = knot.dig("example", "DS", dnssec=True)
......
......@@ -34,7 +34,7 @@ resp.cmp(bind)
# B3. Referral to an Opt-Out Unsigned Zone.
resp = knot.dig("mc.c.example.", "MX", dnssec=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# B4. Wildcard Expansion.
resp = knot.dig("a.z.w.example.", "MX", dnssec=True)
......@@ -44,10 +44,7 @@ resp.cmp(bind)
# B5. Wildcard No Data Error.
resp = knot.dig("a.z.w.example.", "AAAA", dnssec=True)
resp.check(rcode="NOERROR", flags="QR AA", eflags="DO")
# @note Commented out because BIND9 (as of 9.9.4) does not return
# a closest encloser wildcard NSEC3 RR.
# See RFC5155 appendix B.5, page 46
#resp.cmp(bind)
resp.cmp(bind)
# B6. DS Child Zone No Data Error.
resp = knot.dig("example.", "DS", dnssec=True)
......
......@@ -49,12 +49,12 @@ resp.cmp(bind)
# Positive (REFERRAL)
resp = knot.dig("sub.flags", "NS", udp=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA TC AD RA")
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Positive (REFERRAL, below delegation)
resp = knot.dig("ns.sub.flags", "A", udp=True)
resp.check(rcode="NOERROR", flags="QR", noflags="AA TC AD RA")
resp.cmp(bind)
resp.cmp(bind, additional=True)
''' ANY query type. '''
......@@ -68,7 +68,7 @@ resp.cmp(bind)
# ANY to delegation point
resp = knot.dig("sub.flags", "ANY", udp=True)
resp.cmp(bind)
resp.cmp(bind, additional=True)
# ANY to CNAME record
resp = knot.dig("cname.flags", "ANY", udp=True)
......@@ -95,11 +95,11 @@ resp.cmp(bind)
# CNAME leading to delegation
resp = knot.dig("cname-ns.flags", "NS", udp=True)
resp.cmp(bind)
resp.cmp(bind, additional=True)
# CNAME leading below delegation
resp = knot.dig("a.cname-ns.flags", "A", udp=True)
resp.cmp(bind)
resp = knot.dig("cname-below-ns.flags", "A", udp=True)
resp.cmp(bind, additional=True)
# CNAME leading out
resp = knot.dig("cname-out.flags", "A", udp=True)
......@@ -130,7 +130,13 @@ resp = knot.dig("ab.flags", "A", udp=True)
resp.check(rcode="NOERROR")
compare(resp.count(rtype="CNAME", section="answer"), 23, "Count of CNAME records in loop.")
''' CNAME in Additional '''
''' CNAME in MX EXCHANGE. '''
# Knot puts A/AAAA for MX, SRV, and NS into Additional section of the answer.
# However, when the domain name in RDATA is an in-zone CNAME, it doesn't try
# to solve it. We expect that the resolver will be picking only useful
# information from the Additional section and following a CNAME in Additional
# is not simple.
# Leading to existing name
resp = knot.dig("cname-mx.flags", "MX", udp=True)
......@@ -150,7 +156,7 @@ resp.cmp(bind)
''' DNAME answers. '''
# DNAME query
# DNAME query (NODATA)
resp = knot.dig("dname.flags", "A", udp=True)
resp.cmp(bind)
......@@ -246,7 +252,7 @@ resp.cmp(bind)
# Wildcard chain to NS
resp = knot.dig("a.wildcard-deleg.flags", "NS", udp=True)
resp.cmp(bind)
resp.cmp(bind, additional=True)
# Wildcard leading out
resp = knot.dig("a.wildcard-out.flags", "A", udp=True)
......
......@@ -56,6 +56,34 @@ class Response(object):
flag_val = dns.flags.edns_from_text(flag)
isset(not(self.resp.ednsflags & flag_val), "NO %s FLAG" % flag)
def check_rr(self, section="answer", rname=None, rtype=None):
"""
Check for a presence of a RR with given name and type.
"""
section_rrsets = getattr(self.resp, section)
if rname is not None:
rname = dns.name.from_text(rname)
if rtype is not None:
rtype = dns.rdatatype.from_text(rtype)
assert rname or rtype
for rrset in section_rrsets:
if rname is not None and rname != rrset.name:
continue
if rtype is not None and rtype != rrset.rdtype:
continue
break
else:
set_err("CHECK RR PRESENCE")
check_log("ERROR: CHECK RR PRESENCE")
detail_log("!Missing RR name=%s type=%s section=%s" % (
str(rname) if rname is not None else "",
dns.rdatatype.to_text(rtype) if rtype is not None else "",
section
))
detail_log(SEP)
def check_record(self, section="answer", rtype=None, ttl=None, rdata=None,
nordata=None):
'''Checks given section for particular record/rdata'''
......@@ -205,7 +233,7 @@ class Response(object):
compare(option.data.decode('ascii'), nsid, "TXT NSID")
def diff(self, resp, flags=True, answer=True, authority=True,
additional=True):
additional=False):
'''Compares specified response sections against another response'''
if flags:
......@@ -227,8 +255,12 @@ class Response(object):
"ADDITIONAL")
def cmp(self, server, flags=True, answer=True, authority=True,
additional=True):
'''Asks server for the same question an compares specified sections'''
additional=False):
'''
Asks server for the same question an compares specified sections
The Additional section is not compared by default.
'''
resp = server.dig(**self.args)
self.diff(resp, flags, answer, authority, additional)
......@@ -269,6 +301,20 @@ class Response(object):
return cnt
def check_count(self, expected, rtype=None, section="answer"):
found = self.count(rtype, section)
if found != expected:
set_err("CHECK RR COUNT")
check_log("ERROR: CHECK RR COUNT")
detail_log("!Invalid RR count type=%s section=%s" % (
rtype if rtype is not None else "",
section
))
detail_log(SEP)
def check_empty(self, section="answer"):
self.check_count(0, None, section)
def msg_count(self):
'''Returns number of response messages'''
......
......@@ -669,6 +669,9 @@ class Bind(Server):
s.item("max-retry-time", "2")
s.item("transfers-in", "30")
s.item("transfers-out", "30")
s.item("minimal-responses", "true")
s.item("additional-from-auth", "false")
s.item("additional-from-cache", "false")
s.end()
s.begin("key", self.ctlkey.name)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment