mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-27 12:13:20 -04:00
Reimplement 'resolver/ans2' server using AsyncDnsServer
Ensure packet-for-packet compatibility with the old server including
bugs.
(cherry picked from commit d203a39314)
This commit is contained in:
parent
c11a7877b9
commit
aa658f80d7
3 changed files with 225 additions and 185 deletions
|
|
@ -1,184 +0,0 @@
|
|||
#!/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;
|
||||
|
||||
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"));
|
||||
}
|
||||
} 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";
|
||||
}
|
||||
225
bin/tests/system/resolver/ans2/ans.py
Normal file
225
bin/tests/system/resolver/ans2/ans.py
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
"""
|
||||
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.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,
|
||||
)
|
||||
|
||||
|
||||
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 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(),
|
||||
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()
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
import time
|
||||
|
||||
|
||||
import isctest
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue