Added test for the proposed fix

The test works as follows:

1. Client wants to resolve unusual ip6.arpa. name:

   test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. IN TXT

2. Query is sent to ns7, a qmin enabled resolver.

3. ns7 do the first stage in query minimization for the name and send a new
   query to root (ns1):

  _.1.0.0.2.ip6.arpa.        IN  A

4. ns1 delegates ip6.arpa. to ns2.good.:

    ;; AUTHORITY SECTION:
    ;ip6.arpa.      20  IN  NS  ns2.good.

    ;; ADDITIONAL SECTION:
    ;ns2.good.      20  IN  A   10.53.0.2

5. ns7 do a second round in minimizing the name and send a new query
   to ns2.good. (10.53.0.2):

   _.8.2.6.0.1.0.0.2.ip6.arpa.    IN  A

6. ans2 delegates 8.2.6.0.1.0.0.2.ip6.arpa. to ns3.good.:

    ;; AUTHORITY SECTION:
    ;8.2.6.0.1.0.0.2.ip6.arpa. 60   IN  NS  ns3.good.

    ;; ADDITIONAL SECTION:
    ;ns3.good.      60  IN  A   10.53.0.3

7. ns7 do a third round in minimizing the name and send a new query to
   ns3.good.:

    _.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. IN A

8. ans3 delegates 1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. to ns4.good.:

    ;; AUTHORITY SECTION:
    ;1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 60 IN    NS  ns4.good.

    ;; ADDITIONAL SECTION:
    ;ns4.good.      60  IN  A   10.53.0.4

9. ns7 do fourth round in minimizing the name and send a new query to
   ns4.good.:

	_.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.    IN A

10. ns4.good. doesn't know such name, but answers stating it is authoritative for
    the domai:

	;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id:  53815
	...
	;; AUTHORITY SECTION:
	1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 60 IN    SOA ns4.good.  ...

11. ns7 do another minimization on name:
   _.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa
   sends to ns4.good. and gets the same SOA response stated in item #10

12. ns7 do another minimization on name:
	_.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa
	sends to ns4.good. and gets the same SOA response stated in item #10.

13. ns7 do the last query minimization name for the ip6.arpa. QNAME.
	After all IPv6 labels are exausted the algorithm falls back to the
	original QNAME:
	test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa

    ns7 sends a new query with the original QNAME to ans4.

14. Finally ans4 answers with the expected response:
	;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id:  40969
	;; flags: qr aa; QUESTION: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
	;; OPT PSEUDOSECTION:
	; EDNS: version: 0, flags:; udp: 8192
	;; QUESTION SECTION:
	;test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. IN TXT

	;; ANSWER SECTION:
	;test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 1    IN TXT "long_ip6_name"
This commit is contained in:
Diego Fronza
2020-08-28 18:49:26 -03:00
parent 230d79c191
commit 11add69198
4 changed files with 56 additions and 12 deletions

View File

@@ -48,6 +48,7 @@ def logquery(type, qname):
#
# For 1.0.0.2.ip6.arpa it serves
# 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. IN PTR nee.com.
# 8.2.6.0.1.0.0.2.ip6.arpa IN NS ns3.good
# 1.0.0.2.ip6.arpa. IN NS ns2.good
# ip6.arpa. IN NS ns2.good
############################################################################
@@ -76,33 +77,37 @@ def create_response(msg):
if lqname.endswith("1.0.0.2.ip6.arpa."):
# Direct query - give direct answer
if lqname == "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa." and rrtype == PTR:
if lqname.endswith("8.2.6.0.1.0.0.2.ip6.arpa."):
# Delegate to ns3
r.authority.append(dns.rrset.from_text("8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, NS, "ns3.good."))
r.additional.append(dns.rrset.from_text("ns3.good.", 60, IN, A, "10.53.0.3"))
elif lqname == "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa." and rrtype == PTR:
# Direct query - give direct answer
r.answer.append(dns.rrset.from_text("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.", 1, IN, PTR, "nee.com."))
r.flags |= dns.flags.AA
elif lqname == "1.0.0.2.ip6.arpa." and rrtype == NS:
# NS query at the apex
r.answer.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, NS, "ns2.good."))
r.answer.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, NS, "ns2.good."))
r.flags |= dns.flags.AA
elif "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.".endswith(lqname):
# NODATA answer
r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
else:
# NXDOMAIN
r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.set_rcode(NXDOMAIN)
return r
elif lqname.endswith("ip6.arpa."):
if lqname == "ip6.arpa." and rrtype == NS:
# NS query at the apex
r.answer.append(dns.rrset.from_text("ip6.arpa.", 1, IN, NS, "ns2.good."))
r.answer.append(dns.rrset.from_text("ip6.arpa.", 30, IN, NS, "ns2.good."))
r.flags |= dns.flags.AA
elif "1.0.0.2.ip6.arpa.".endswith(lqname):
# NODATA answer
r.authority.append(dns.rrset.from_text("ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.authority.append(dns.rrset.from_text("ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
else:
# NXDOMAIN
r.authority.append(dns.rrset.from_text("ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.authority.append(dns.rrset.from_text("ip6.arpa.", 30, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.set_rcode(NXDOMAIN)
return r
elif lqname.endswith("bad."):
@@ -134,19 +139,25 @@ def create_response(msg):
r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2"))
r.flags |= dns.flags.AA
elif lqname == "" and rrtype == NS:
r.answer.append(dns.rrset.from_text(suffix, 1, IN, NS, "ns2." + suffix))
r.answer.append(dns.rrset.from_text(suffix, 30, IN, NS, "ns2." + suffix))
r.flags |= dns.flags.AA
elif lqname == "ns2." and rrtype == A:
r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, A, "10.53.0.2"))
r.answer.append(dns.rrset.from_text("ns2."+suffix, 30, IN, A, "10.53.0.2"))
r.flags |= dns.flags.AA
elif lqname == "ns2." and rrtype == AAAA:
r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::2"))
r.answer.append(dns.rrset.from_text("ns2."+suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::2"))
r.flags |= dns.flags.AA
elif lqname == "ns3." and rrtype == A:
r.answer.append(dns.rrset.from_text("ns3."+suffix, 1, IN, A, "10.53.0.3"))
r.answer.append(dns.rrset.from_text("ns3."+suffix, 30, IN, A, "10.53.0.3"))
r.flags |= dns.flags.AA
elif lqname == "ns3." and rrtype == AAAA:
r.answer.append(dns.rrset.from_text("ns3."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::3"))
r.answer.append(dns.rrset.from_text("ns3."+suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::3"))
r.flags |= dns.flags.AA
elif lqname == "ns4." and rrtype == A:
r.answer.append(dns.rrset.from_text("ns4."+suffix, 30, IN, A, "10.53.0.4"))
r.flags |= dns.flags.AA
elif lqname == "ns4." and rrtype == AAAA:
r.answer.append(dns.rrset.from_text("ns4."+suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::4"))
r.flags |= dns.flags.AA
elif lqname == "a.bit.longer.ns.name." and rrtype == A:
r.answer.append(dns.rrset.from_text("a.bit.longer.ns.name."+suffix, 1, IN, A, "10.53.0.4"))

View File

@@ -67,6 +67,8 @@ def create_response(msg):
r = dns.message.make_response(m)
r.set_rcode(NOERROR)
ip6req = False
if lqname.endswith("bad."):
bad = True
suffix = "bad."
@@ -82,6 +84,8 @@ def create_response(msg):
slow = True
suffix = "slow."
lqname = lqname[:-5]
elif lqname.endswith("8.2.6.0.1.0.0.2.ip6.arpa."):
ip6req = True
else:
r.set_rcode(REFUSED)
return r
@@ -101,6 +105,9 @@ def create_response(msg):
elif lqname.endswith("zoop.boing."):
r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, SOA, "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
r.set_rcode(NXDOMAIN)
elif ip6req:
r.authority.append(dns.rrset.from_text("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, NS, "ns4.good."))
r.additional.append(dns.rrset.from_text("ns4.good.", 60, IN, A, "10.53.0.4"))
else:
r.set_rcode(REFUSED)

View File

@@ -68,6 +68,8 @@ def create_response(msg):
r = dns.message.make_response(m)
r.set_rcode(NOERROR)
ip6req = False
if lqname.endswith("bad."):
bad = True
suffix = "bad."
@@ -83,6 +85,8 @@ def create_response(msg):
slow = True
suffix = "slow."
lqname = lqname[:-5]
elif lqname.endswith("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa."):
ip6req = True
else:
r.set_rcode(REFUSED)
return r
@@ -103,6 +107,17 @@ def create_response(msg):
r.set_rcode(NXDOMAIN)
if ugly:
r.set_rcode(FORMERR)
elif ip6req:
r.flags |= dns.flags.AA
if lqname == "test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa." and rrtype == TXT:
r.answer.append(dns.rrset.from_text("test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 1, IN, TXT, "long_ip6_name"))
elif "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.".endswith(lqname):
#NODATA answer
r.authority.append(dns.rrset.from_text("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, SOA, "ns4.good. hostmaster.arpa. 2018050100 120 30 320 16"))
else:
# NXDOMAIN
r.authority.append(dns.rrset.from_text("1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, SOA, "ns4.good. hostmaster.arpa. 2018050100 120 30 320 16"))
r.set_rcode(NXDOMAIN)
else:
r.set_rcode(REFUSED)

View File

@@ -383,5 +383,16 @@ for ans in ans2; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true;
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "qname minimization resolves unusual ip6.arpa. names ($n)"
ret=0
$CLEANQL
$DIG $DIGOPTS test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. txt @10.53.0.7 > dig.out.test$n 2>&1
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
# Expected output in dig.out.test$n:
# ;; ANSWER SECTION:
# test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 1 IN TXT "long_ip6_name"
grep 'ip6\.arpa.*TXT.*long_ip6_name' dig.out.test$n > /dev/null || ret=1
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1