+++ /dev/null
-#!/usr/bin/perl
-
-# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
-#
-# SPDX-License-Identifier: MPL-2.0
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, you can obtain one at https://mozilla.org/MPL/2.0/.
-#
-# See the COPYRIGHT file distributed with this work for additional
-# information regarding copyright ownership.
-
-#
-# Ad hoc name server
-#
-
-use IO::File;
-use IO::Socket;
-use Net::DNS;
-use Net::DNS::Packet;
-
-print "Using Net::DNS $Net::DNS::VERSION\n";
-
-my $localport = int($ENV{'PORT'});
-if (!$localport) { $localport = 5300; }
-
-my $sock = IO::Socket::INET->new(LocalAddr => "10.53.0.2",
- LocalPort => $localport, Proto => "udp") or die "$!";
-
-my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
-print $pidf "$$\n" or die "cannot write pid file: $!";
-$pidf->close or die "cannot close pid file: $!";
-sub rmpid { unlink "ans.pid"; exit 1; };
-
-$SIG{INT} = \&rmpid;
-$SIG{TERM} = \&rmpid;
-
-for (;;) {
- $sock->recv($buf, 512);
-
- print "**** request from " , $sock->peerhost, " port ", $sock->peerport, "\n";
-
- my $packet;
-
- if ($Net::DNS::VERSION > 0.68) {
- $packet = new Net::DNS::Packet(\$buf, 0);
- $@ and die $@;
- } else {
- my $err;
- ($packet, $err) = new Net::DNS::Packet(\$buf, 0);
- $err and die $err;
- }
-
- print "REQUEST:\n";
- $packet->print;
-
- $packet->header->qr(1);
-
- my @questions = $packet->question;
- my $qname = $questions[0]->qname;
- my $qtype = $questions[0]->qtype;
-
- if ($qname eq "com" && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("com 300 NS a.root-servers.nil."));
- } elsif ($qname eq "example.com" && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("example.com 300 NS a.root-servers.nil."));
- } elsif ($qname eq "cname1.example.com") {
- # Data for the "cname + other data / 1" test
- $packet->push("answer", new Net::DNS::RR("cname1.example.com 300 CNAME cname1.example.com"));
- $packet->push("answer", new Net::DNS::RR("cname1.example.com 300 A 1.2.3.4"));
- } elsif ($qname eq "cname2.example.com") {
- # Data for the "cname + other data / 2" test: same RRs in opposite order
- $packet->push("answer", new Net::DNS::RR("cname2.example.com 300 A 1.2.3.4"));
- $packet->push("answer", new Net::DNS::RR("cname2.example.com 300 CNAME cname2.example.com"));
- } elsif ($qname =~ /redirect\.com/) {
- $packet->push("authority", new Net::DNS::RR("redirect.com 300 NS ns.redirect.com"));
- $packet->push("additional", new Net::DNS::RR("ns.redirect.com 300 A 10.53.0.6"));
- } elsif ($qname =~ /\.tld1/) {
- $packet->push("authority", new Net::DNS::RR("tld1 300 NS ns.tld1"));
- $packet->push("additional", new Net::DNS::RR("ns.tld1 300 A 10.53.0.6"));
- } elsif ($qname =~ /\.tld2/) {
- $packet->push("authority", new Net::DNS::RR("tld2 300 NS ns.tld2"));
- $packet->push("additional", new Net::DNS::RR("ns.tld2 300 A 10.53.0.7"));
- } elsif ($qname eq "org" && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("org 300 NS a.root-servers.nil."));
- } elsif ($qname eq "example.org" && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("example.org 300 NS a.root-servers.nil."));
- } elsif (($qname eq "baddname.example.org" || $qname eq "gooddname.example.org") && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("example.org 300 NS a.root-servers.nil."));
- } elsif ($qname eq "www.example.org" ||
- $qname eq "badcname.example.org" ||
- $qname eq "goodcname.example.org" ||
- $qname eq "foo.baddname.example.org" ||
- $qname eq "foo.gooddname.example.org") {
- # Data for address/alias filtering.
- $packet->header->aa(1);
- if ($qtype eq "A") {
- $packet->push("answer",
- new Net::DNS::RR($qname .
- " 300 A 192.0.2.1"));
- } elsif ($qtype eq "AAAA") {
- $packet->push("answer",
- new Net::DNS::RR($qname .
- " 300 AAAA 2001:db8:beef::1"));
- }
- } elsif ($qname eq "net" && $qtype eq "NS") {
- $packet->header->aa(1);
- $packet->push("answer", new Net::DNS::RR("net 300 NS a.root-servers.nil."));
- } elsif ($qname eq "noresponse.exampleudp.net") {
- next;
- } elsif ($qname =~ /example\.net/) {
- $packet->push("authority", new Net::DNS::RR("example.net 300 NS ns.example.net"));
- $packet->push("additional", new Net::DNS::RR("ns.example.net 300 A 10.53.0.3"));
- } elsif ($qname =~ /exampleudp\.net/) {
- $packet->push("authority", new Net::DNS::RR("exampleudp.net 300 NS ns.exampleudp.net"));
- $packet->push("additional", new Net::DNS::RR("ns.exampleudp.net 300 A 10.53.0.2"));
- } elsif ($qname =~ /lame\.example\.org/) {
- $packet->header->ad(0);
- $packet->header->aa(0);
- $packet->push("authority", new Net::DNS::RR("lame.example.org 300 NS ns.lame.example.org"));
- $packet->push("additional", new Net::DNS::RR("ns.lame.example.org 300 A 10.53.0.3"));
- } elsif ($qname =~ /sub\.example\.org/) {
- # Data for CNAME/DNAME filtering. The final answers are
- # expected to be accepted regardless of the filter setting.
- $packet->push("authority", new Net::DNS::RR("sub.example.org 300 NS ns.sub.example.org"));
- $packet->push("additional", new Net::DNS::RR("ns.sub.example.org 300 A 10.53.0.3"));
- } elsif ($qname =~ /glue-in-answer\.example\.org/) {
- $packet->push("answer", new Net::DNS::RR("ns.glue-in-answer.example.org 300 A 10.53.0.3"));
- $packet->push("authority", new Net::DNS::RR("glue-in-answer.example.org 300 NS ns.glue-in-answer.example.org"));
- $packet->push("additional", new Net::DNS::RR("ns.glue-in-answer.example.org 300 A 10.53.0.3"));
- } elsif ($qname =~ /\.broken/ || $qname =~ /^broken/) {
- # Delegation to broken TLD.
- $packet->push("authority", new Net::DNS::RR("broken 300 NS ns.broken"));
- $packet->push("additional", new Net::DNS::RR("ns.broken 300 A 10.53.0.4"));
- } elsif ($qname =~ /\.partial-formerr/) {
- $packet->header->rcode("FORMERR");
- } elsif ($qname eq "gl6412") {
- if ($qtype eq "SOA") {
- $packet->push("answer",
- new Net::DNS::RR($qname . " 300 SOA . . 0 0 0 0 0"));
- } elsif ($qtype eq "NS") {
- $packet->push("answer",
- new Net::DNS::RR($qname . " 300 NS ns2" . $qname));
- $packet->push("answer",
- new Net::DNS::RR($qname . " 300 NS ns3" . $qname));
- } else {
- $packet->push("authority",
- new Net::DNS::RR($qname . " 300 SOA . . 0 0 0 0 0"));
- }
- } elsif ($qname eq "a.gl6412" || $qname eq "a.a.gl6412") {
- $packet->push("authority",
- new Net::DNS::RR($qname . " 300 SOA . . 0 0 0 0 0"));
- } elsif ($qname eq "ns2.gl6412") {
- if ($qtype eq "A") {
- $packet->push("answer",
- new Net::DNS::RR($qname . " 300 A 10.53.0.2"));
- } else {
- $packet->push("authority",
- new Net::DNS::RR($qname . " 300 SOA . . 0 0 0 0 0"));
- }
- } elsif ($qname eq "ns3.gl6412") {
- if ($qtype eq "A") {
- $packet->push("answer",
- new Net::DNS::RR($qname . " 300 A 10.53.0.3"));
- } else {
- $packet->push("authority",
- new Net::DNS::RR($qname . " 300 SOA . . 0 0 0 0 0"));
- }
- } elsif ($qname eq "zoneversion") {
- $packet->push("authority", new Net::DNS::RR(". 300 SOA . . 0 0 0 0 0"));
- if ($Net::DNS::VERSION >= 1.49) {
- $packet->edns->option('ZONEVERSION' => [0, 1, '01022304'] )
- } elsif ($Net::DNS::VERSION >= 1.35) {
- $packet->edns->option('19' => {'BASE16' => '000101022304'} )
- } else {
- $packet->edns->option('19' => pack 'H*', '000101022304')
- }
- } else {
- # Data for the "bogus referrals" test
- $packet->push("authority", new Net::DNS::RR("below.www.example.com 300 NS ns.below.www.example.com"));
- $packet->push("additional", new Net::DNS::RR("ns.below.www.example.com 300 A 10.53.0.3"));
- }
-
- $sock->send($packet->data);
-
- print "RESPONSE:\n";
- $packet->print;
- print "\n";
-}
--- /dev/null
+"""
+Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+
+SPDX-License-Identifier: MPL-2.0
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, you can obtain one at https://mozilla.org/MPL/2.0/.
+
+See the COPYRIGHT file distributed with this work for additional
+information regarding copyright ownership.
+"""
+
+from typing import AsyncGenerator, Tuple, Union
+
+import dns.edns
+import dns.name
+import dns.rcode
+import dns.rrset
+import dns.rdatatype
+
+from isctest.asyncserver import (
+ AsyncDnsServer,
+ DnsResponseSend,
+ DomainHandler,
+ IgnoreAllQueries,
+ QnameHandler,
+ QnameQtypeHandler,
+ QueryContext,
+ StaticResponseHandler,
+ ResponseHandler,
+)
+
+from resolver_ans import (
+ DelegationHandler,
+ Gl6412AHandler,
+ Gl6412Handler,
+ Gl6412Ns2Handler,
+ Gl6412Ns3Handler,
+ rrset,
+ setup_delegation,
+ soa_rrset,
+)
+
+
+class BadGoodDnameNsHandler(QnameQtypeHandler, StaticResponseHandler):
+ qnames = [
+ "baddname.example.org.",
+ "gooddname.example.org.",
+ ]
+ qtypes = [dns.rdatatype.NS]
+ answer = [rrset("example.org.", dns.rdatatype.NS, "a.root-servers.nil.")]
+ authoritative = True
+
+
+def _cname_rrsets(
+ qname: Union[dns.name.Name, str],
+) -> Tuple[dns.rrset.RRset, dns.rrset.RRset]:
+ return (
+ rrset(qname, dns.rdatatype.CNAME, f"{qname}"),
+ rrset(qname, dns.rdatatype.A, "1.2.3.4"),
+ )
+
+
+class Cname1Handler(QnameHandler, StaticResponseHandler):
+ qnames = ["cname1.example.com."]
+ # Data for the "cname + other data / 1" test
+ answer = _cname_rrsets(qnames[0])
+ authoritative = False
+
+
+class Cname2Handler(QnameHandler, StaticResponseHandler):
+ qnames = ["cname2.example.com."]
+ # Data for the "cname + other data / 2" test: same RRs in opposite order
+ answer = tuple(reversed(_cname_rrsets(qnames[0])))
+ authoritative = False
+
+
+class ExampleOrgHandler(QnameHandler):
+ qnames = [
+ "www.example.org",
+ "badcname.example.org",
+ "goodcname.example.org",
+ "foo.baddname.example.org",
+ "foo.gooddname.example.org",
+ ]
+
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[DnsResponseSend, None]:
+ # Data for address/alias filtering.
+ if qctx.qtype == dns.rdatatype.A:
+ a_rrset = rrset(qctx.qname, dns.rdatatype.A, "192.0.2.1")
+ qctx.response.answer.append(a_rrset)
+ elif qctx.qtype == dns.rdatatype.AAAA:
+ aaaa_rrset = rrset(qctx.qname, dns.rdatatype.AAAA, "2001:db8:beef::1")
+ qctx.response.answer.append(aaaa_rrset)
+ yield DnsResponseSend(qctx.response, authoritative=True)
+
+
+class NoResponseExampleUdpHandler(QnameHandler, IgnoreAllQueries):
+ qnames = ["noresponse.exampleudp.net."]
+
+
+class RootNsHandler(QnameQtypeHandler):
+ qnames = [
+ "example.com.",
+ "com.",
+ "example.org.",
+ "org.",
+ "net.",
+ ]
+
+ qtypes = [dns.rdatatype.NS]
+
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[DnsResponseSend, None]:
+ root_ns = rrset(qctx.qname, dns.rdatatype.NS, "a.root-servers.nil.")
+ qctx.response.answer.append(root_ns)
+ yield DnsResponseSend(qctx.response, authoritative=True)
+
+
+class ZoneVersionHandler(QnameHandler):
+ qnames = ["zoneversion."]
+
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[DnsResponseSend, None]:
+ qctx.response.authority.append(soa_rrset("."))
+ zoneversion_opt = dns.edns.GenericOption(19, bytes.fromhex("000101022304")) # type: ignore
+ qctx.response.use_edns(edns=0, options=[zoneversion_opt])
+ yield DnsResponseSend(qctx.response, authoritative=False)
+
+
+class Ns2Delegation(DelegationHandler):
+ domains = ["exampleudp.net."]
+ server_number = 2
+
+
+class Ns3Delegation(DelegationHandler):
+ domains = [
+ "example.net.",
+ "lame.example.org.",
+ "sub.example.org.",
+ ]
+ server_number = 3
+
+
+class Ns3GlueInAnswerDelegation(DelegationHandler):
+ domains = ["glue-in-answer.example.org."]
+ server_number = 3
+
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[DnsResponseSend, None]:
+ async for dns_response in super().get_responses(qctx):
+ dns_response.response.answer += dns_response.response.additional
+ yield dns_response
+
+
+class Ns4Delegation(DelegationHandler):
+ domains = ["broken."]
+ server_number = 4
+
+
+class Ns6Delegation(DelegationHandler):
+ domains = [
+ "redirect.com.",
+ "tld1.",
+ ]
+ server_number = 6
+
+
+class Ns7Delegation(DelegationHandler):
+ domains = ["tld2."]
+ server_number = 7
+
+
+class PartialFormerrHandler(DomainHandler, StaticResponseHandler):
+ domains = ["partial-formerr."]
+ authoritative = False
+ rcode = dns.rcode.FORMERR
+
+
+class FallbackHandler(ResponseHandler):
+ async def get_responses(
+ self, qctx: QueryContext
+ ) -> AsyncGenerator[DnsResponseSend, None]:
+ setup_delegation(qctx, "below.www.example.com.", 3)
+ yield DnsResponseSend(qctx.response, authoritative=False)
+
+
+# XXX: This handler is here to provide bug-for-bug compatibility with the old server.
+class XXXBuggyTldNsHandler(QnameQtypeHandler, FallbackHandler):
+ qnames = [
+ "tld1.",
+ "tld2.",
+ ]
+
+ qtypes = [dns.rdatatype.NS]
+
+
+def main() -> None:
+ server = AsyncDnsServer(default_rcode=dns.rcode.NOERROR)
+
+ # Install QnameHandlers first
+ server.install_response_handlers(
+ BadGoodDnameNsHandler(),
+ Cname1Handler(),
+ Cname2Handler(),
+ ExampleOrgHandler(),
+ Gl6412AHandler(),
+ Gl6412Handler(),
+ Gl6412Ns2Handler(),
+ Gl6412Ns3Handler(),
+ NoResponseExampleUdpHandler(),
+ RootNsHandler(),
+ ZoneVersionHandler(),
+ XXXBuggyTldNsHandler(),
+ )
+
+ # Then install DomainHandlers
+ server.install_response_handlers(
+ Ns2Delegation(),
+ Ns3Delegation(),
+ Ns3GlueInAnswerDelegation(),
+ Ns4Delegation(),
+ Ns6Delegation(),
+ Ns7Delegation(),
+ PartialFormerrHandler(),
+ )
+
+ # Finally, install the fallback handler
+ server.install_response_handler(FallbackHandler())
+ server.run()
+
+
+if __name__ == "__main__":
+ main()