1085 lines
38 KiB
Diff
1085 lines
38 KiB
Diff
From 3b7a43eb2fcdec7e31587a88464f5eb8df5c23d6 Mon Sep 17 00:00:00 2001
|
|
From: Evan Hunt <each@isc.org>
|
|
Date: Mon, 29 Sep 2025 22:17:39 -0700
|
|
Subject: [PATCH] Tighten restrictions on caching NS RRsets in authority
|
|
section
|
|
|
|
To prevent certain spoofing attacks, a new check has been added
|
|
to the existing rules for whether NS data can be cached: the owner
|
|
name of the NS RRset must be an ancestor of the name being queried.
|
|
|
|
(cherry picked from commit fa153f791f9324bf84abf8d259e11c0531fe6e25)
|
|
(cherry picked from commit 41ab0709d1bde6fb8a2dde623d00e69bc48fab0d)
|
|
|
|
Further restrict addresses that are cached when processing referrals
|
|
|
|
Use the owner name of the NS record as the bailwick apex name
|
|
when determining which additional records to cache, rather than
|
|
the name of the delegating zone (or a parent thereof).
|
|
|
|
(cherry picked from commit a41054e9e606a61f1b3c8bc0c54e2f1059347165)
|
|
(cherry picked from commit eba8e3eb33f907a1a622c065138e19b087b6e4f1)
|
|
|
|
Retry lookups with unsigned DNAME over TCP
|
|
|
|
To prevent spoofed unsigned DNAME responses being accepted retry
|
|
response with unsigned DNAMEs over TCP if the response is not TSIG
|
|
signed or there isn't a good DNS CLIENT COOKIE.
|
|
|
|
To prevent test failures, this required adding TCP support to the
|
|
ans3 and ans4 servers in the chain system test.
|
|
|
|
(cherry picked from commit 2e40705c06831988106335ed77db3cf924d431f6)
|
|
(cherry picked from commit 0e4cd87bed5efc61443337034a9d96287b4885dc)
|
|
---
|
|
bin/tests/system/chain/ans3/ans.pl | 129 -----------------
|
|
bin/tests/system/chain/ans3/ans.py | 216 ++++++++++++++++++++++++++++
|
|
bin/tests/system/chain/ans4/ans.py | 58 ++++++--
|
|
bin/tests/system/cookie/ans9/ans.py | 139 ++++++++++--------
|
|
lib/dns/include/dns/message.h | 8 ++
|
|
lib/dns/message.c | 12 ++
|
|
lib/dns/resolver.c | 84 ++++++++---
|
|
lib/dns/win32/libdns.def.in | 1 +
|
|
8 files changed, 432 insertions(+), 215 deletions(-)
|
|
delete mode 100644 bin/tests/system/chain/ans3/ans.pl
|
|
create mode 100644 bin/tests/system/chain/ans3/ans.py
|
|
|
|
diff --git a/bin/tests/system/chain/ans3/ans.pl b/bin/tests/system/chain/ans3/ans.pl
|
|
deleted file mode 100644
|
|
index 31d22d807b..0000000000
|
|
--- a/bin/tests/system/chain/ans3/ans.pl
|
|
+++ /dev/null
|
|
@@ -1,129 +0,0 @@
|
|
-#!/usr/bin/env perl
|
|
-#
|
|
-# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
-#
|
|
-# 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.
|
|
-
|
|
-use strict;
|
|
-use warnings;
|
|
-
|
|
-use IO::File;
|
|
-use Getopt::Long;
|
|
-use Net::DNS::Nameserver;
|
|
-
|
|
-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;
|
|
-
|
|
-my $localaddr = "10.53.0.3";
|
|
-
|
|
-my $localport = int($ENV{'PORT'});
|
|
-if (!$localport) { $localport = 5300; }
|
|
-
|
|
-my $verbose = 0;
|
|
-my $ttl = 60;
|
|
-my $zone = "example.broken";
|
|
-my $nsname = "ns3.$zone";
|
|
-my $synth = "synth-then-dname.$zone";
|
|
-my $synth2 = "synth2-then-dname.$zone";
|
|
-
|
|
-sub reply_handler {
|
|
- my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
|
|
- my ($rcode, @ans, @auth, @add);
|
|
-
|
|
- print ("request: $qname/$qtype\n");
|
|
- STDOUT->flush();
|
|
-
|
|
- if ($qname eq "example.broken") {
|
|
- if ($qtype eq "SOA") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
|
|
- push @ans, $rr;
|
|
- } elsif ($qtype eq "NS") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
|
|
- push @add, $rr;
|
|
- }
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "cname-to-$synth2") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
|
|
- push @ans, $rr;
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "$synth" || $qname eq "$synth2") {
|
|
- if ($qtype eq "DNAME") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME .");
|
|
- push @ans, $rr;
|
|
- }
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "name.$synth") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("$synth $ttl $qclass DNAME .");
|
|
- push @ans, $rr;
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "name.$synth2") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
|
|
- push @ans, $rr;
|
|
- $rcode = "NOERROR";
|
|
- # The following three code branches referring to the "example.dname"
|
|
- # zone are necessary for the resolver variant of the CVE-2021-25215
|
|
- # regression test to work. A named instance cannot be used for
|
|
- # serving the DNAME records below as a version of BIND vulnerable to
|
|
- # CVE-2021-25215 would crash while answering the queries asked by
|
|
- # the tested resolver.
|
|
- } elsif ($qname eq "ns3.example.dname") {
|
|
- if ($qtype eq "A") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass A 10.53.0.3");
|
|
- push @ans, $rr;
|
|
- }
|
|
- if ($qtype eq "AAAA") {
|
|
- my $rr = new Net::DNS::RR("example.dname. $ttl $qclass SOA . . 0 0 0 0 $ttl");
|
|
- push @auth, $rr;
|
|
- }
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "self.example.self.example.dname") {
|
|
- my $rr = new Net::DNS::RR("self.example.dname. $ttl $qclass DNAME dname.");
|
|
- push @ans, $rr;
|
|
- $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME self.example.dname.");
|
|
- push @ans, $rr;
|
|
- $rcode = "NOERROR";
|
|
- } elsif ($qname eq "self.example.dname") {
|
|
- if ($qtype eq "DNAME") {
|
|
- my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME dname.");
|
|
- push @ans, $rr;
|
|
- }
|
|
- $rcode = "NOERROR";
|
|
- } else {
|
|
- $rcode = "REFUSED";
|
|
- }
|
|
- return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
|
|
-}
|
|
-
|
|
-GetOptions(
|
|
- 'port=i' => \$localport,
|
|
- 'verbose!' => \$verbose,
|
|
-);
|
|
-
|
|
-my $ns = Net::DNS::Nameserver->new(
|
|
- LocalAddr => $localaddr,
|
|
- LocalPort => $localport,
|
|
- ReplyHandler => \&reply_handler,
|
|
- Verbose => $verbose,
|
|
-);
|
|
-
|
|
-$ns->main_loop;
|
|
diff --git a/bin/tests/system/chain/ans3/ans.py b/bin/tests/system/chain/ans3/ans.py
|
|
new file mode 100644
|
|
index 0000000000..d6d4eea6d9
|
|
--- /dev/null
|
|
+++ b/bin/tests/system/chain/ans3/ans.py
|
|
@@ -0,0 +1,216 @@
|
|
+# 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.
|
|
+
|
|
+############################################################################
|
|
+# ans.py: See README.anspy for details.
|
|
+############################################################################
|
|
+
|
|
+from __future__ import print_function
|
|
+import os
|
|
+import sys
|
|
+import signal
|
|
+import socket
|
|
+import select
|
|
+from datetime import datetime, timedelta
|
|
+import functools
|
|
+
|
|
+import dns, dns.message, dns.query
|
|
+from dns.rdatatype import *
|
|
+from dns.rdataclass import *
|
|
+from dns.rcode import *
|
|
+from dns.name import *
|
|
+
|
|
+############################################################################
|
|
+# Respond to a DNS query.
|
|
+############################################################################
|
|
+def create_response(msg):
|
|
+ ttl = 60
|
|
+ zone = "example.broken."
|
|
+ nsname = "ns3." + zone
|
|
+ synth = "synth-then-dname." + zone
|
|
+ synth2 = "synth2-then-dname." + zone
|
|
+
|
|
+ m = dns.message.from_wire(msg)
|
|
+ qname = m.question[0].name.to_text()
|
|
+
|
|
+ # prepare the response and convert to wire format
|
|
+ r = dns.message.make_response(m)
|
|
+
|
|
+ # get qtype
|
|
+ rrtype = m.question[0].rdtype
|
|
+ qtype = dns.rdatatype.to_text(rrtype)
|
|
+ print("request: " + qname + "/" + qtype)
|
|
+
|
|
+ rcode = "NOERROR"
|
|
+ if qname == zone:
|
|
+ if qtype == "SOA":
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, SOA, ". . 0 0 0 0 0"))
|
|
+ elif qtype == "NS":
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, NS, nsname))
|
|
+ r.additional.append(dns.rrset.from_text(nsname, ttl, IN, A, ip4))
|
|
+ elif qname == "cname-to-" + synth2:
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name." + synth2))
|
|
+ r.answer.append(dns.rrset.from_text("name." + synth2, ttl, IN, CNAME, "name."))
|
|
+ r.answer.append(dns.rrset.from_text(synth2, ttl, IN, DNAME, "."))
|
|
+ elif qname == synth or qname == synth2:
|
|
+ if qtype == "DNAME":
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, DNAME, "."))
|
|
+ elif qname == "name." + synth:
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name."))
|
|
+ r.answer.append(dns.rrset.from_text(synth, ttl, IN, DNAME, "."))
|
|
+ elif qname == "name." + synth2:
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, CNAME, "name."))
|
|
+ r.answer.append(dns.rrset.from_text(synth2, ttl, IN, DNAME, "."))
|
|
+ elif qname == "ns3.example.dname.":
|
|
+ # This and the next two code branches referring to the "example.dname"
|
|
+ # zone are necessary for the resolver variant of the CVE-2021-25215
|
|
+ # regression test to work. A named instance cannot be used for
|
|
+ # serving the DNAME records below as a version of BIND vulnerable to
|
|
+ # CVE-2021-25215 would crash while answering the queries asked by
|
|
+ # the tested resolver.
|
|
+ if qtype == "A":
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, A, ip4))
|
|
+ elif qtype == "AAAA":
|
|
+ r.authority.append(
|
|
+ dns.rrset.from_text("example.dname.", ttl, IN, SOA, ". . 0 0 0 0 0")
|
|
+ )
|
|
+ elif qname == "self.example.self..example.dname.":
|
|
+ r.answer.append(
|
|
+ dns.rrset.from_text("self.example.dname.", ttl, IN, DNAME, "dname.")
|
|
+ )
|
|
+ r.answer.append(
|
|
+ dns.rrset.from_text(qname, ttl, IN, CNAME, "self.example.dname.")
|
|
+ )
|
|
+ elif qname == "self.example.dname.":
|
|
+ if qtype == "DNAME":
|
|
+ r.answer.append(dns.rrset.from_text(qname, ttl, IN, DNAME, "dname."))
|
|
+ else:
|
|
+ rcode = "REFUSED"
|
|
+
|
|
+ r.flags |= dns.flags.AA
|
|
+ r.use_edns()
|
|
+ return r.to_wire()
|
|
+
|
|
+
|
|
+def sigterm(signum, frame):
|
|
+ print("Shutting down now...")
|
|
+ os.remove("ans.pid")
|
|
+ running = False
|
|
+ sys.exit(0)
|
|
+
|
|
+
|
|
+############################################################################
|
|
+# Main
|
|
+#
|
|
+# Set up responder and control channel, open the pid file, and start
|
|
+# the main loop, listening for queries on the query channel or commands
|
|
+# on the control channel and acting on them.
|
|
+############################################################################
|
|
+ip4 = "10.53.0.3"
|
|
+ip6 = "fd92:7065:b8e:ffff::3"
|
|
+
|
|
+try:
|
|
+ port = int(os.environ["PORT"])
|
|
+except:
|
|
+ port = 5300
|
|
+
|
|
+query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
+query4_udp.bind((ip4, port))
|
|
+
|
|
+query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
+query4_tcp.bind((ip4, port))
|
|
+query4_tcp.listen(1)
|
|
+query4_tcp.settimeout(1)
|
|
+
|
|
+havev6 = True
|
|
+try:
|
|
+ query6_udp = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
+ try:
|
|
+ query6_udp.bind((ip6, port))
|
|
+ except:
|
|
+ query6_udp.close()
|
|
+ havev6 = False
|
|
+
|
|
+ query6_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
+ try:
|
|
+ query6_tcp.bind((ip4, port))
|
|
+ query6_tcp.listen(1)
|
|
+ query6_tcp.settimeout(1)
|
|
+ except:
|
|
+ query6_tcp.close()
|
|
+ havev6 = False
|
|
+except:
|
|
+ havev6 = False
|
|
+
|
|
+signal.signal(signal.SIGTERM, sigterm)
|
|
+
|
|
+f = open("ans.pid", "w")
|
|
+pid = os.getpid()
|
|
+print(pid, file=f)
|
|
+f.close()
|
|
+
|
|
+running = True
|
|
+
|
|
+print("Listening on %s port %d" % (ip4, port))
|
|
+if havev6:
|
|
+ print("Listening on %s port %d" % (ip6, port))
|
|
+print("Ctrl-c to quit")
|
|
+
|
|
+if havev6:
|
|
+ input = [query4_udp, query4_tcp, query6_udp, query6_tcp]
|
|
+else:
|
|
+ input = [query4_udp, query4_tcp]
|
|
+
|
|
+while running:
|
|
+ try:
|
|
+ inputready, outputready, exceptready = select.select(input, [], [])
|
|
+ except select.error as e:
|
|
+ break
|
|
+ except socket.error as e:
|
|
+ break
|
|
+ except KeyboardInterrupt:
|
|
+ break
|
|
+
|
|
+ for s in inputready:
|
|
+ if s == query4_udp or s == query6_udp:
|
|
+ print("Query received on %s" % (ip4 if s == query4_udp else ip6))
|
|
+ # Handle incoming queries
|
|
+ msg = s.recvfrom(65535)
|
|
+ rsp = create_response(msg[0])
|
|
+ if rsp:
|
|
+ s.sendto(rsp, msg[1])
|
|
+ elif s == query4_tcp or s == query6_tcp:
|
|
+ try:
|
|
+ conn, _ = s.accept()
|
|
+ if s == query4_tcp or s == query6_tcp:
|
|
+ print(
|
|
+ "TCP Query received on %s" % (ip4 if s == query4_tcp else ip6),
|
|
+ end=" ",
|
|
+ )
|
|
+ # get TCP message length
|
|
+ msg = conn.recv(2)
|
|
+ if len(msg) != 2:
|
|
+ print("couldn't read TCP message length")
|
|
+ continue
|
|
+ length = struct.unpack(">H", msg[:2])[0]
|
|
+ msg = conn.recv(length)
|
|
+ if len(msg) != length:
|
|
+ print("couldn't read TCP message")
|
|
+ continue
|
|
+ rsp = create_response(msg)
|
|
+ if rsp:
|
|
+ conn.send(struct.pack(">H", len(rsp)))
|
|
+ conn.send(rsp)
|
|
+ conn.close()
|
|
+ except socket.error as e:
|
|
+ print("error: %s" % str(e))
|
|
+ if not running:
|
|
+ break
|
|
diff --git a/bin/tests/system/chain/ans4/ans.py b/bin/tests/system/chain/ans4/ans.py
|
|
index 45d650417f..e4fc15a280 100755
|
|
--- a/bin/tests/system/chain/ans4/ans.py
|
|
+++ b/bin/tests/system/chain/ans4/ans.py
|
|
@@ -276,16 +276,30 @@ except: port=5300
|
|
try: ctrlport=int(os.environ['EXTRAPORT1'])
|
|
except: ctrlport=5300
|
|
|
|
-query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
-query4_socket.bind((ip4, port))
|
|
+query4_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
+query4_udp.bind((ip4, port))
|
|
+
|
|
+query4_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
+query4_tcp.bind((ip4, port))
|
|
+query4_tcp.listen(1)
|
|
+query4_tcp.settimeout(1)
|
|
|
|
havev6 = True
|
|
try:
|
|
- query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
+ query6_udp = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
|
+ try:
|
|
+ query6_udp.bind((ip6, port))
|
|
+ except:
|
|
+ query6_udp.close()
|
|
+ havev6 = False
|
|
+
|
|
+ query6_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
try:
|
|
- query6_socket.bind((ip6, port))
|
|
+ query6_tcp.bind((ip4, port))
|
|
+ query6_tcp.listen(1)
|
|
+ query6_tcp.settimeout(1)
|
|
except:
|
|
- query6_socket.close()
|
|
+ query6_tcp.close()
|
|
havev6 = False
|
|
except:
|
|
havev6 = False
|
|
@@ -310,9 +324,9 @@ print ("Control channel on %s port %d" % (ip4, ctrlport))
|
|
print ("Ctrl-c to quit")
|
|
|
|
if havev6:
|
|
- input = [query4_socket, query6_socket, ctrl_socket]
|
|
+ input = [query4_udp, query4_tcp, query6_udp, query6_tcp, ctrl_socket]
|
|
else:
|
|
- input = [query4_socket, ctrl_socket]
|
|
+ input = [query4_udp, query4_tcp, ctrl_socket]
|
|
|
|
while running:
|
|
try:
|
|
@@ -335,13 +349,37 @@ while running:
|
|
break
|
|
ctl_channel(msg)
|
|
conn.close()
|
|
- if s == query4_socket or s == query6_socket:
|
|
- print ("Query received on %s" %
|
|
- (ip4 if s == query4_socket else ip6))
|
|
+ elif s == query4_udp or s == query6_udp:
|
|
+ print("Query received on %s" % (ip4 if s == query4_udp else ip6))
|
|
# Handle incoming queries
|
|
msg = s.recvfrom(65535)
|
|
rsp = create_response(msg[0])
|
|
if rsp:
|
|
s.sendto(rsp, msg[1])
|
|
+ elif s == query4_tcp or s == query6_tcp:
|
|
+ try:
|
|
+ conn, _ = s.accept()
|
|
+ if s == query4_tcp or s == query6_tcp:
|
|
+ print(
|
|
+ "TCP Query received on %s" % (ip4 if s == query4_tcp else ip6),
|
|
+ end=" ",
|
|
+ )
|
|
+ # get TCP message length
|
|
+ msg = conn.recv(2)
|
|
+ if len(msg) != 2:
|
|
+ print("couldn't read TCP message length")
|
|
+ continue
|
|
+ length = struct.unpack(">H", msg[:2])[0]
|
|
+ msg = conn.recv(length)
|
|
+ if len(msg) != length:
|
|
+ print("couldn't read TCP message")
|
|
+ continue
|
|
+ rsp = create_response(msg)
|
|
+ if rsp:
|
|
+ conn.send(struct.pack(">H", len(rsp)))
|
|
+ conn.send(rsp)
|
|
+ conn.close()
|
|
+ except socket.error as e:
|
|
+ print("error: %s" % str(e))
|
|
if not running:
|
|
break
|
|
diff --git a/bin/tests/system/cookie/ans9/ans.py b/bin/tests/system/cookie/ans9/ans.py
|
|
index b454fc8c91..dd48bdb095 100644
|
|
--- a/bin/tests/system/cookie/ans9/ans.py
|
|
+++ b/bin/tests/system/cookie/ans9/ans.py
|
|
@@ -1,13 +1,13 @@
|
|
-############################################################################
|
|
# 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
|
|
+# 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 __future__ import print_function
|
|
import os
|
|
@@ -35,28 +35,27 @@ from dns.rdataclass import *
|
|
from dns.rdatatype import *
|
|
from dns.tsig import *
|
|
|
|
+
|
|
# Log query to file
|
|
def logquery(type, qname):
|
|
with open("qlog", "a") as f:
|
|
f.write("%s %s\n", type, qname)
|
|
|
|
+
|
|
# DNS 2.0 keyring specifies the algorithm
|
|
try:
|
|
- keyring = dns.tsigkeyring.from_text({ "foo" : {
|
|
- "hmac-sha256",
|
|
- "aaaaaaaaaaaa"
|
|
- } ,
|
|
- "fake" : {
|
|
- "hmac-sha256",
|
|
- "aaaaaaaaaaaa"
|
|
- }
|
|
- })
|
|
+ keyring = dns.tsigkeyring.from_text(
|
|
+ {
|
|
+ "foo": {"hmac-sha256", "aaaaaaaaaaaa"},
|
|
+ "fake": {"hmac-sha256", "aaaaaaaaaaaa"},
|
|
+ }
|
|
+ )
|
|
except:
|
|
- keyring = dns.tsigkeyring.from_text({ "foo" : "aaaaaaaaaaaa",
|
|
- "fake" : "aaaaaaaaaaaa" })
|
|
+ keyring = dns.tsigkeyring.from_text({"foo": "aaaaaaaaaaaa", "fake": "aaaaaaaaaaaa"})
|
|
|
|
dopass2 = False
|
|
|
|
+
|
|
############################################################################
|
|
#
|
|
# This server will serve valid and spoofed answers. A spoofed answer will
|
|
@@ -81,7 +80,7 @@ def create_response(msg, tcp, first, ns10):
|
|
m = dns.message.from_wire(msg, keyring=keyring)
|
|
qname = m.question[0].name.to_text()
|
|
lqname = qname.lower()
|
|
- labels = lqname.split('.')
|
|
+ labels = lqname.split(".")
|
|
rrtype = m.question[0].rdtype
|
|
typename = dns.rdatatype.to_text(rrtype)
|
|
|
|
@@ -113,27 +112,35 @@ def create_response(msg, tcp, first, ns10):
|
|
# Add a server cookie to the response
|
|
if labels[0] != "nocookie":
|
|
for o in m.options:
|
|
- if o.otype == 10: # Use 10 instead of COOKIE
|
|
- if first and labels[0] == "withtsig" and not tcp:
|
|
- r.use_tsig(keyring = keyring,
|
|
- keyname = dns.name.from_text("fake"),
|
|
- algorithm = HMAC_SHA256)
|
|
- elif labels[0] != "tcponly" or tcp:
|
|
- cookie = o
|
|
- if len(o.data) == 8:
|
|
- cookie.data = o.data + o.data
|
|
- else:
|
|
- cookie.data = o.data
|
|
- r.use_edns(options=[cookie])
|
|
+ if o.otype == 10: # Use 10 instead of COOKIE
|
|
+ if first and labels[0] == "withtsig" and not tcp:
|
|
+ r.use_tsig(
|
|
+ keyring=keyring,
|
|
+ keyname=dns.name.from_text("fake"),
|
|
+ algorithm=HMAC_SHA256,
|
|
+ )
|
|
+ elif labels[0] != "tcponly" or tcp:
|
|
+ cookie = o
|
|
+ try:
|
|
+ if len(o.server) == 0:
|
|
+ cookie.server = o.client
|
|
+ except AttributeError: # dnspython<2.7.0 compat
|
|
+ if len(o.data) == 8:
|
|
+ cookie.data = o.data + o.data
|
|
+ else:
|
|
+ cookie.data = o.data
|
|
+ r.use_edns(options=[cookie])
|
|
r.flags |= dns.flags.AA
|
|
return r
|
|
|
|
+
|
|
def sigterm(signum, frame):
|
|
- print ("Shutting down now...")
|
|
- os.remove('ans.pid')
|
|
+ print("Shutting down now...")
|
|
+ os.remove("ans.pid")
|
|
running = False
|
|
sys.exit(0)
|
|
|
|
+
|
|
############################################################################
|
|
# Main
|
|
#
|
|
@@ -146,8 +153,10 @@ ip4_addr2 = "10.53.0.10"
|
|
ip6_addr1 = "fd92:7065:b8e:ffff::9"
|
|
ip6_addr2 = "fd92:7065:b8e:ffff::10"
|
|
|
|
-try: port=int(os.environ['PORT'])
|
|
-except: port=5300
|
|
+try:
|
|
+ port = int(os.environ["PORT"])
|
|
+except:
|
|
+ port = 5300
|
|
|
|
query4_udp1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
query4_udp1.bind((ip4_addr1, port))
|
|
@@ -195,24 +204,32 @@ except:
|
|
|
|
signal.signal(signal.SIGTERM, sigterm)
|
|
|
|
-f = open('ans.pid', 'w')
|
|
+f = open("ans.pid", "w")
|
|
pid = os.getpid()
|
|
-print (pid, file=f)
|
|
+print(pid, file=f)
|
|
f.close()
|
|
|
|
running = True
|
|
|
|
-print ("Using DNS version %s" % dns.version.version)
|
|
-print ("Listening on %s port %d" % (ip4_addr1, port))
|
|
-print ("Listening on %s port %d" % (ip4_addr2, port))
|
|
+print("Using DNS version %s" % dns.version.version)
|
|
+print("Listening on %s port %d" % (ip4_addr1, port))
|
|
+print("Listening on %s port %d" % (ip4_addr2, port))
|
|
if havev6:
|
|
- print ("Listening on %s port %d" % (ip6_addr1, port))
|
|
- print ("Listening on %s port %d" % (ip6_addr2, port))
|
|
-print ("Ctrl-c to quit")
|
|
+ print("Listening on %s port %d" % (ip6_addr1, port))
|
|
+ print("Listening on %s port %d" % (ip6_addr2, port))
|
|
+print("Ctrl-c to quit")
|
|
|
|
if havev6:
|
|
- input = [query4_udp1, query6_udp1, query4_tcp1, query6_tcp1,
|
|
- query4_udp2, query6_udp2, query4_tcp2, query6_tcp2]
|
|
+ input = [
|
|
+ query4_udp1,
|
|
+ query6_udp1,
|
|
+ query4_tcp1,
|
|
+ query6_tcp1,
|
|
+ query4_udp2,
|
|
+ query6_udp2,
|
|
+ query4_tcp2,
|
|
+ query6_tcp2,
|
|
+ ]
|
|
else:
|
|
input = [query4_udp1, query4_tcp1, query4_udp2, query4_tcp2]
|
|
|
|
@@ -228,14 +245,19 @@ while running:
|
|
|
|
for s in inputready:
|
|
ns10 = False
|
|
- if s == query4_udp1 or s == query6_udp1 or \
|
|
- s == query4_udp2 or s == query6_udp2:
|
|
+ if s == query4_udp1 or s == query6_udp1 or s == query4_udp2 or s == query6_udp2:
|
|
if s == query4_udp1 or s == query6_udp1:
|
|
- print ("UDP Query received on %s" %
|
|
- (ip4_addr1 if s == query4_udp1 else ip6_addr1), end=" ")
|
|
+ print(
|
|
+ "UDP Query received on %s"
|
|
+ % (ip4_addr1 if s == query4_udp1 else ip6_addr1),
|
|
+ end=" ",
|
|
+ )
|
|
if s == query4_udp2 or s == query6_udp2:
|
|
- print ("UDP Query received on %s" %
|
|
- (ip4_addr2 if s == query4_udp2 else ip6_addr2), end=" ")
|
|
+ print(
|
|
+ "UDP Query received on %s"
|
|
+ % (ip4_addr2 if s == query4_udp2 else ip6_addr2),
|
|
+ end=" ",
|
|
+ )
|
|
ns10 = True
|
|
# Handle incoming queries
|
|
msg = s.recvfrom(65535)
|
|
@@ -244,31 +266,36 @@ while running:
|
|
print(dns.rcode.to_text(rsp.rcode()))
|
|
s.sendto(rsp.to_wire(), msg[1])
|
|
if dopass2:
|
|
- print ("Sending second UDP response without TSIG", end=" ")
|
|
+ print("Sending second UDP response without TSIG", end=" ")
|
|
rsp = create_response(msg[0], False, False, ns10)
|
|
s.sendto(rsp.to_wire(), msg[1])
|
|
print(dns.rcode.to_text(rsp.rcode()))
|
|
|
|
- if s == query4_tcp1 or s == query6_tcp1 or \
|
|
- s == query4_tcp2 or s == query6_tcp2:
|
|
+ if s == query4_tcp1 or s == query6_tcp1 or s == query4_tcp2 or s == query6_tcp2:
|
|
try:
|
|
(cs, _) = s.accept()
|
|
if s == query4_tcp1 or s == query6_tcp1:
|
|
- print ("TCP Query received on %s" %
|
|
- (ip4_addr1 if s == query4_tcp1 else ip6_addr1), end=" ")
|
|
+ print(
|
|
+ "TCP Query received on %s"
|
|
+ % (ip4_addr1 if s == query4_tcp1 else ip6_addr1),
|
|
+ end=" ",
|
|
+ )
|
|
if s == query4_tcp2 or s == query6_tcp2:
|
|
- print ("TCP Query received on %s" %
|
|
- (ip4_addr2 if s == query4_tcp2 else ip6_addr2), end=" ")
|
|
+ print(
|
|
+ "TCP Query received on %s"
|
|
+ % (ip4_addr2 if s == query4_tcp2 else ip6_addr2),
|
|
+ end=" ",
|
|
+ )
|
|
ns10 = True
|
|
# get TCP message length
|
|
buf = cs.recv(2)
|
|
- length = struct.unpack('>H', buf[:2])[0]
|
|
+ length = struct.unpack(">H", buf[:2])[0]
|
|
# grep DNS message
|
|
msg = cs.recv(length)
|
|
rsp = create_response(msg, True, True, ns10)
|
|
print(dns.rcode.to_text(rsp.rcode()))
|
|
wire = rsp.to_wire()
|
|
- cs.send(struct.pack('>H', len(wire)))
|
|
+ cs.send(struct.pack(">H", len(wire)))
|
|
cs.send(wire)
|
|
cs.close()
|
|
except s.timeout:
|
|
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
|
|
index 96c5ef1f79..54208a49e6 100644
|
|
--- a/lib/dns/include/dns/message.h
|
|
+++ b/lib/dns/include/dns/message.h
|
|
@@ -226,6 +226,7 @@ struct dns_message {
|
|
unsigned int cc_bad : 1;
|
|
unsigned int tkey : 1;
|
|
unsigned int rdclass_set : 1;
|
|
+ unsigned int has_dname : 1;
|
|
|
|
unsigned int opt_reserved;
|
|
unsigned int sig_reserved;
|
|
@@ -1408,6 +1409,13 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass);
|
|
* \li msg be a valid message with parsing intent.
|
|
*/
|
|
|
|
+bool
|
|
+dns_message_hasdname(dns_message_t *msg);
|
|
+/*%<
|
|
+ * Return whether a DNAME was detected in the ANSWER section of a QUERY
|
|
+ * message when it was parsed.
|
|
+ */
|
|
+
|
|
ISC_LANG_ENDDECLS
|
|
|
|
#endif /* DNS_MESSAGE_H */
|
|
diff --git a/lib/dns/message.c b/lib/dns/message.c
|
|
index 9ea2b9e7fb..5603bcadf9 100644
|
|
--- a/lib/dns/message.c
|
|
+++ b/lib/dns/message.c
|
|
@@ -459,6 +459,7 @@ msginit(dns_message_t *m) {
|
|
m->cc_bad = 0;
|
|
m->tkey = 0;
|
|
m->rdclass_set = 0;
|
|
+ m->has_dname = 0;
|
|
m->querytsig = NULL;
|
|
m->indent.string = dns_master_indentstr;
|
|
m->indent.count = dns_master_indent;
|
|
@@ -1829,6 +1830,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
|
|
*/
|
|
msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
|
|
free_name = false;
|
|
+ } else if (rdtype == dns_rdatatype_dname &&
|
|
+ sectionid == DNS_SECTION_ANSWER &&
|
|
+ msg->opcode == dns_opcode_query)
|
|
+ {
|
|
+ msg->has_dname = 1;
|
|
}
|
|
rdataset = NULL;
|
|
|
|
@@ -4699,3 +4705,9 @@ dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
|
|
msg->rdclass = rdclass;
|
|
msg->rdclass_set = 1;
|
|
}
|
|
+
|
|
+bool
|
|
+dns_message_hasdname(dns_message_t *msg) {
|
|
+ REQUIRE(DNS_MESSAGE_VALID(msg));
|
|
+ return msg->has_dname;
|
|
+}
|
|
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
|
|
index 487107614c..1c704551aa 100644
|
|
--- a/lib/dns/resolver.c
|
|
+++ b/lib/dns/resolver.c
|
|
@@ -428,6 +428,7 @@ typedef struct {
|
|
typedef struct {
|
|
fetchctx_t * fctx;
|
|
dns_message_t * rmessage;
|
|
+ dns_name_t * ns_name;
|
|
} dns_chkarg_t;
|
|
|
|
struct dns_fetch {
|
|
@@ -6259,7 +6260,9 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset,
|
|
* locally served zone.
|
|
*/
|
|
static inline bool
|
|
-name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
|
|
+name_external(dns_name_t *name, dns_name_t *ns_name, dns_rdatatype_t type,
|
|
+ fetchctx_t *fctx)
|
|
+{
|
|
isc_result_t result;
|
|
dns_forwarders_t *forwarders = NULL;
|
|
dns_fixedname_t fixed, zfixed;
|
|
@@ -6277,7 +6280,9 @@ name_external(dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) {
|
|
int _orderp = 0;
|
|
unsigned int _nlabelsp = 0;
|
|
|
|
- apex = ISFORWARDER(fctx->addrinfo) ? fctx->fwdname : &fctx->domain;
|
|
+ apex = ISFORWARDER(fctx->addrinfo)
|
|
+ ? fctx->fwdname
|
|
+ : (ns_name != NULL) ? ns_name : &fctx->domain;
|
|
|
|
/*
|
|
* The name is outside the queried namespace.
|
|
@@ -6386,7 +6391,7 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
|
|
result = dns_message_findname(rmessage, section, addname,
|
|
dns_rdatatype_any, 0, &name, NULL);
|
|
if (result == ISC_R_SUCCESS) {
|
|
- external = name_external(name, type, fctx);
|
|
+ external = name_external(name, chkarg->ns_name, type, fctx);
|
|
if (type == dns_rdatatype_a) {
|
|
for (rdataset = ISC_LIST_HEAD(name->list);
|
|
rdataset != NULL;
|
|
@@ -6460,7 +6465,7 @@ chase_additional(fetchctx_t *fctx, dns_message_t *rmessage) {
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link)) {
|
|
if (CHASE(rdataset)) {
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
chkarg.fctx = fctx;
|
|
chkarg.rmessage = rmessage;
|
|
rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
|
|
@@ -7068,7 +7073,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
|
|
* we're not following a chain.)
|
|
*/
|
|
if (!negative_response && ns_name != NULL && oqname == NULL) {
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
/*
|
|
* We already know ns_name is a subdomain of fctx->domain.
|
|
* If ns_name is equal to fctx->domain, we're not making
|
|
@@ -7100,6 +7105,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
|
|
FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING);
|
|
chkarg.fctx = fctx;
|
|
chkarg.rmessage = message;
|
|
+ chkarg.ns_name = ns_name;
|
|
(void)dns_rdataset_additionaldata(ns_rdataset, check_related,
|
|
&chkarg);
|
|
#if CHECK_FOR_GLUE_IN_ANSWER
|
|
@@ -7113,7 +7119,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
|
|
if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 &&
|
|
(fctx->type == dns_rdatatype_aaaa ||
|
|
fctx->type == dns_rdatatype_a)) {
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
chkarg.fcx = fctx;
|
|
chkarg.rmessage = message;
|
|
(void)dns_rdataset_additionaldata(ns_rdataset,
|
|
@@ -7193,8 +7199,9 @@ validinanswer(dns_rdataset_t *rdataset, fetchctx_t *fctx) {
|
|
}
|
|
|
|
static isc_result_t
|
|
-answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
+answer_response(resquery_t *query, dns_message_t *message) {
|
|
isc_result_t result;
|
|
+ fetchctx_t *fctx = NULL;
|
|
dns_name_t *name = NULL, *qname = NULL, *ns_name = NULL;
|
|
dns_name_t *aname = NULL, *cname = NULL, *dname = NULL;
|
|
dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
|
|
@@ -7207,6 +7214,8 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
dns_view_t *view = NULL;
|
|
dns_trust_t trust;
|
|
|
|
+ REQUIRE(VALID_QUERY(query));
|
|
+ fctx = query->fctx;
|
|
REQUIRE(VALID_FCTX(fctx));
|
|
|
|
FCTXTRACE("answer_response");
|
|
@@ -7271,7 +7280,9 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
/*
|
|
* Don't accept DNAME from parent namespace.
|
|
*/
|
|
- if (name_external(name, dns_rdatatype_dname, fctx)) {
|
|
+ if (name_external(name, NULL, dns_rdatatype_dname,
|
|
+ fctx))
|
|
+ {
|
|
continue;
|
|
}
|
|
|
|
@@ -7326,7 +7337,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
rdataset != NULL;
|
|
rdataset = ISC_LIST_NEXT(rdataset, link))
|
|
{
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
if (!validinanswer(rdataset, fctx)) {
|
|
return (DNS_R_FORMERR);
|
|
}
|
|
@@ -7357,12 +7368,13 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
|
|
chkarg.fctx = fctx;
|
|
chkarg.rmessage = message;
|
|
+ chkarg.ns_name = ns_name;
|
|
(void)dns_rdataset_additionaldata(rdataset,
|
|
check_related,
|
|
&chkarg);
|
|
}
|
|
} else if (aname != NULL) {
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
if (!validinanswer(ardataset, fctx))
|
|
return (DNS_R_FORMERR);
|
|
if ((ardataset->type == dns_rdatatype_a ||
|
|
@@ -7386,6 +7398,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
ardataset->trust = trust;
|
|
chkarg.fctx = fctx;
|
|
chkarg.rmessage = message;
|
|
+ chkarg.ns_name = ns_name;
|
|
(void)dns_rdataset_additionaldata(ardataset, check_related,
|
|
&chkarg);
|
|
for (sigrdataset = ISC_LIST_HEAD(aname->list);
|
|
@@ -7507,12 +7520,23 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
*
|
|
* We expect there to be only one owner name for all the rdatasets
|
|
* in this section, and we expect that it is not external.
|
|
+ *
|
|
+ * If the message was not sent over TCP or otherwise secured,
|
|
+ * skip this.
|
|
*/
|
|
- result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
|
|
+ if (message->cc_ok || message->tsig != NULL || message->sig0 != NULL ||
|
|
+ (query->options & DNS_FETCHOPT_TCP) != 0)
|
|
+ {
|
|
+ result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
|
|
+ } else {
|
|
+ done = true;
|
|
+ }
|
|
while (!done && result == ISC_R_SUCCESS) {
|
|
name = NULL;
|
|
dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
|
|
- if (!name_external(name, dns_rdatatype_ns, fctx)) {
|
|
+ if (!name_external(name, ns_name, dns_rdatatype_ns, fctx) &&
|
|
+ dns_name_issubdomain(&fctx->name, name))
|
|
+ {
|
|
/*
|
|
* We expect to find NS or SIG NS rdatasets, and
|
|
* nothing else.
|
|
@@ -7523,7 +7547,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
if (rdataset->type == dns_rdatatype_ns ||
|
|
(rdataset->type == dns_rdatatype_rrsig &&
|
|
rdataset->covers == dns_rdatatype_ns)) {
|
|
- dns_chkarg_t chkarg;
|
|
+ dns_chkarg_t chkarg = { 0 };
|
|
name->attributes |=
|
|
DNS_NAMEATTR_CACHE;
|
|
rdataset->attributes |=
|
|
@@ -7547,6 +7571,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
|
|
*/
|
|
chkarg.fctx = fctx;
|
|
chkarg.rmessage = message;
|
|
+ chkarg.ns_name = ns_name;
|
|
(void)dns_rdataset_additionaldata(
|
|
rdataset,
|
|
check_related,
|
|
@@ -7988,6 +8013,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
unsigned int bucketnum;
|
|
dns_resolver_t *res;
|
|
bool bucket_empty;
|
|
+ bool secured = false;
|
|
#ifdef HAVE_DNSTAP
|
|
isc_socket_t *sock = NULL;
|
|
isc_sockaddr_t localaddr, *la = NULL;
|
|
@@ -8298,6 +8324,17 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
goto done;
|
|
}
|
|
|
|
+ /*
|
|
+ * Remember whether this message was signed or had a
|
|
+ * valid client cookie; if not, we may need to retry over
|
|
+ * TCP later.
|
|
+ */
|
|
+ if (rmessage->cc_ok || rmessage->tsig != NULL ||
|
|
+ rmessage->sig0 != NULL)
|
|
+ {
|
|
+ secured = true;
|
|
+ }
|
|
+
|
|
/*
|
|
* The dispatcher should ensure we only get responses with QR set.
|
|
*/
|
|
@@ -8315,10 +8352,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
* This may be a misconfigured anycast server or an attempt to send
|
|
* a spoofed response. Skip if we have a valid tsig.
|
|
*/
|
|
- if (dns_message_gettsig(rmessage, NULL) == NULL &&
|
|
- !rmessage->cc_ok && !rmessage->cc_bad &&
|
|
- (options & DNS_FETCHOPT_TCP) == 0)
|
|
- {
|
|
+ if (!secured && (options & DNS_FETCHOPT_TCP) == 0) {
|
|
unsigned char cookie[COOKIE_BUFFER_SIZE];
|
|
if (dns_adb_getcookie(fctx->adb, query->addrinfo, cookie,
|
|
sizeof(cookie)) > CLIENT_COOKIE_SIZE)
|
|
@@ -8341,6 +8375,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
* XXXMPA When support for DNS COOKIE becomes ubiquitous, fall
|
|
* back to TCP for all non-COOKIE responses.
|
|
*/
|
|
+
|
|
+ /*
|
|
+ * Check whether we need to retry over TCP for some other
|
|
+ * reason.
|
|
+ */
|
|
+ if (dns_message_hasdname(rmessage)) {
|
|
+ options |= DNS_FETCHOPT_TCP;
|
|
+ resend = true;
|
|
+ goto done;
|
|
+ }
|
|
}
|
|
|
|
/*
|
|
@@ -8697,7 +8741,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
if ((rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
|
|
ISFORWARDER(query->addrinfo))
|
|
{
|
|
- result = answer_response(fctx, rmessage);
|
|
+ result = answer_response(query, rmessage);
|
|
if (result != ISC_R_SUCCESS)
|
|
FCTXTRACE3("answer_response (AA/fwd)", result);
|
|
} else if (iscname(fctx, rmessage) &&
|
|
@@ -8709,7 +8753,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
* answer when a CNAME is followed. We should treat
|
|
* it as a valid answer.
|
|
*/
|
|
- result = answer_response(fctx, rmessage);
|
|
+ result = answer_response(query, rmessage);
|
|
if (result != ISC_R_SUCCESS)
|
|
FCTXTRACE3("answer_response (!ANY/!CNAME)",
|
|
result);
|
|
@@ -8718,7 +8762,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
|
|
/*
|
|
* Lame response !!!.
|
|
*/
|
|
- result = answer_response(fctx, rmessage);
|
|
+ result = answer_response(query, rmessage);
|
|
if (result != ISC_R_SUCCESS)
|
|
FCTXTRACE3("answer_response (!NS)", result);
|
|
} else {
|
|
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
|
|
index 7320653439..b48bc89bbd 100644
|
|
--- a/lib/dns/win32/libdns.def.in
|
|
+++ b/lib/dns/win32/libdns.def.in
|
|
@@ -537,6 +537,7 @@ dns_message_gettemprdataset
|
|
dns_message_gettimeadjust
|
|
dns_message_gettsig
|
|
dns_message_gettsigkey
|
|
+dns_message_hasdname
|
|
dns_message_logfmtpacket
|
|
dns_message_logfmtpacket2
|
|
dns_message_logpacket
|
|
--
|
|
2.51.1
|
|
|