+++ /dev/null
-#!/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; };
-sub term { };
-
-$SIG{INT} = \&rmpid;
-if ($Net::DNS::VERSION > 1.41) {
- $SIG{TERM} = \&term;
-} else {
- $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,
-);
-
-if ($Net::DNS::VERSION >= 1.42) {
- $ns->start_server();
- select(undef, undef, undef, undef);
- $ns->stop_server();
- unlink "ans.pid";
-} else {
- $ns->main_loop;
-}
--- /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.
+
+############################################################################
+# 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
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
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:
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
-############################################################################
# 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
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
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)
# 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
#
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))
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]
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)
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:
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;
* \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 */
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;
*/
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;
msg->rdclass = rdclass;
msg->rdclass_set = 1;
}
+
+bool
+dns_message_hasdname(dns_message_t *msg) {
+ REQUIRE(DNS_MESSAGE_VALID(msg));
+ return msg->has_dname;
+}
}
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;
dns_view_t *view = NULL;
dns_trust_t trust;
+ REQUIRE(VALID_QUERY(query));
+ fctx = query->fctx;
REQUIRE(VALID_FCTX(fctx));
FCTXTRACE("answer_response");
*
* 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);
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;
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.
*/
* 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)
* 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;
+ }
}
/*
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) &&
* 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);
/*
* Lame response !!!.
*/
- result = answer_response(fctx, rmessage);
+ result = answer_response(query, rmessage);
if (result != ISC_R_SUCCESS)
FCTXTRACE3("answer_response (!NS)", result);
} else {
dns_message_gettimeadjust
dns_message_gettsig
dns_message_gettsigkey
+dns_message_hasdname
dns_message_logfmtpacket
dns_message_logfmtpacket2
dns_message_logpacket