QNAME minimization

This commit is contained in:
Witold Kręcicki 2018-05-09 12:02:11 +02:00
parent b7968f6c25
commit 0698158eb0
20 changed files with 762 additions and 12 deletions

View file

@ -185,6 +185,8 @@ options {\n\
provide-ixfr true;\n\
query-source address *;\n\
query-source-v6 address *;\n\
qname-minimization no;\n\
qname-minimization-strict no;\n\
recursion true;\n\
request-expire true;\n\
request-ixfr true;\n\

View file

@ -4639,6 +4639,16 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
INSIST(result == ISC_R_SUCCESS);
view->recursion = cfg_obj_asboolean(obj);
obj = NULL;
result = named_config_get(maps, "qname-minimization", &obj);
INSIST(result == ISC_R_SUCCESS);
view->qminimization = cfg_obj_asboolean(obj);
obj = NULL;
result = named_config_get(maps, "qname-minimization-strict", &obj);
INSIST(result == ISC_R_SUCCESS);
view->qmin_strict = cfg_obj_asboolean(obj);
obj = NULL;
result = named_config_get(maps, "auth-nxdomain", &obj);
INSIST(result == ISC_R_SUCCESS);

View file

@ -0,0 +1,206 @@
############################################################################
# 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 http://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
import sys
import signal
import socket
import select
from datetime import datetime, timedelta
import time
import functools
import dns, dns.message, dns.query
from dns.rdatatype import *
from dns.rdataclass import *
from dns.rcode import *
from dns.name import *
# Log query to file
def logquery(type, qname):
with open("qlog", "a") as f:
f.write("%s %s\n", type, qname)
############################################################################
# Respond to a DNS query.
# For good. it serves:
# ns2.good. IN A 10.53.0.2
# icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.1
# it responds properly (with NODATA empty response) to non-empty terminals
#
# For slow. it works the same as for good., but each response is delayed by 400 miliseconds
#
# For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals
#
# 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.
# 1.0.0.2.ip6.arpa. IN NS ns2.good
# ip6.arpa. IN NS ns2.good
############################################################################
def create_response(msg):
m = dns.message.from_wire(msg)
qname = m.question[0].name.to_text()
lqname = qname.lower()
labels = lqname.split('.')
# get qtype
rrtype = m.question[0].rdtype
typename = dns.rdatatype.to_text(rrtype)
bad = False
slow = False
# log this query
with open("query.log", "a") as f:
f.write("%s %s\n" % (typename, lqname))
print("%s %s" % (typename, lqname), end=" ")
r = dns.message.make_response(m)
r.set_rcode(NOERROR)
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:
# 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."))
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."))
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"))
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.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."))
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"))
else:
# NXDOMAIN
r.authority.append(dns.rrset.from_text("ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
r.set_rcode(NXDOMAIN)
return r
elif lqname.endswith("bad."):
bad = True
suffix = "bad."
lqname = lqname[:-4]
elif lqname.endswith("good."):
suffix = "good."
lqname = lqname[:-5]
elif lqname.endswith("slow."):
slow = True
suffix = "slow."
lqname = lqname[:-5]
else:
r.set_rcode(REFUSED)
return r
# Good/bad differs only in how we treat non-empty terminals
if lqname == "icky.icky.icky.ptang.zoop.boing." and rrtype == A:
r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.1"))
elif lqname == "" and rrtype == NS:
r.answer.append(dns.rrset.from_text(suffix, 1, IN, NS, "ns2." + suffix))
elif lqname == "ns2." and rrtype == A:
r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, A, "10.53.0.2"))
elif lqname == "ns2." and rrtype == AAAA:
r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::2"))
else:
r.authority.append(dns.rrset.from_text(suffix, 1, IN, SOA, "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
if bad or not "icky.icky.icky.ptang.zoop.boing.".endswith(lqname):
r.set_rcode(NXDOMAIN)
if slow:
time.sleep(0.4)
return r
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.2"
ip6 = "fd92:7065:b8e:ffff::2"
try: port=int(os.environ['PORT'])
except: port=5300
query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
query4_socket.bind((ip4, port))
havev6 = True
try:
query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
try:
query6_socket.bind((ip6, port))
except:
query6_socket.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_socket, query6_socket]
else:
input = [query4_socket]
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_socket or s == query6_socket:
print ("Query received on %s" %
(ip4 if s == query4_socket else ip6), end=" ")
# Handle incoming queries
msg = s.recvfrom(65535)
rsp = create_response(msg[0])
if rsp:
print(dns.rcode.to_text(rsp.rcode()))
s.sendto(rsp.to_wire(), msg[1])
else:
print("NO RESPONSE")
if not running:
break

View file

@ -0,0 +1,17 @@
#!/bin/sh
#
# 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 http://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
rm -f ns*/named.conf
rm -f */named.memstats
rm -f */named.run
rm -f dig.out.*
rm -f ns*/named.lock
rm -f ans2/query.log

View file

@ -0,0 +1,29 @@
/*
* 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 http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
// NS1
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
transfer-source 10.53.0.1;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion no;
notify yes;
};
zone "." {
type master;
file "root.db";
};

View file

@ -0,0 +1,32 @@
; 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 http://mozilla.org/MPL/2.0/.
;
; See the COPYRIGHT file distributed with this work for additional
; information regarding copyright ownership.
$TTL 20
. IN SOA wpk.isc.org. a.root.servers.nil. (
2000042100 ; serial
600 ; refresh
600 ; retry
1200 ; expire
2 ; minimum
)
. NS a.root-servers.nil.
a.root-servers.nil. A 10.53.0.1
ip6.arpa. NS ns2.good.
good. NS ns2.good.
ns2.good. A 10.53.0.2
bad. NS ns2.bad.
ns2.bad. A 10.53.0.2
slow NS ns2.slow.
ns2.slow. A 10.53.0.2
horrible. NS ns2.horrible.
ns2.horrible. A 10.53.0.2

View file

@ -0,0 +1,41 @@
/*
* 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 http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
// NS3
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
qname-minimization no;
qname-minimization-strict no;
querylog yes;
resolver-query-timeout 30;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "../../common/root.hint";
};

View file

@ -0,0 +1,41 @@
/*
* 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 http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
// NS4
options {
query-source address 10.53.0.4;
notify-source 10.53.0.4;
transfer-source 10.53.0.4;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
recursion yes;
qname-minimization yes;
qname-minimization-strict yes;
querylog yes;
resolver-query-timeout 30;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "../../common/root.hint";
};

View file

@ -0,0 +1,41 @@
/*
* 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 http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
// NS5
options {
query-source address 10.53.0.5;
notify-source 10.53.0.5;
transfer-source 10.53.0.5;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.5; };
listen-on-v6 { none; };
recursion yes;
qname-minimization yes;
qname-minimization-strict no;
querylog yes;
resolver-query-timeout 30;
};
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
zone "." {
type hint;
file "../../common/root.hint";
};

View file

@ -0,0 +1,20 @@
#!/bin/sh -e
#
# 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 http://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
$SHELL clean.sh
copy_setports ns1/named.conf.in ns1/named.conf
copy_setports ns3/named.conf.in ns3/named.conf
copy_setports ns4/named.conf.in ns4/named.conf
copy_setports ns5/named.conf.in ns5/named.conf

View file

@ -0,0 +1,138 @@
#!/bin/sh
#
# 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 http://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
DIGOPTS="-p ${PORT}"
RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p ${CONTROLPORT} -s"
CLEANQL="rm -f ans2/query.log"
status=0
n=0
n=`expr $n + 1`
echo_i "query for .good is not minimized when qname-minimization is off ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.3 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.3 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
echo "A icky.icky.icky.ptang.zoop.boing.good." | diff ans2/query.log - > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .bad is not minimized when qname-minimization is off ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.3 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.3 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans2/query.log - > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .slow is not minimized when qname-minimization is off ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.3 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.3 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
echo "A icky.icky.icky.ptang.zoop.boing.slow." | diff ans2/query.log - > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .good is properly minimized when qname-minimization is on ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.4 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.4 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
NS boing.good.
NS zoop.boing.good.
NS ptang.zoop.boing.good.
NS icky.ptang.zoop.boing.good.
NS icky.icky.ptang.zoop.boing.good.
A icky.icky.icky.ptang.zoop.boing.good.
__EOF
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .bad fails when qname-minimization is on and in strict mode ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.4 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.4 > dig.out.test$n
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
echo "NS boing.bad." | diff ans2/query.log - > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .bad succeds when qname-minimization is on and not in strict mode ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.5 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.5 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
NS boing.bad.
NS zoop.boing.bad.
NS ptang.zoop.boing.bad.
NS icky.ptang.zoop.boing.bad.
NS icky.icky.ptang.zoop.boing.bad.
A icky.icky.icky.ptang.zoop.boing.bad.
__EOF
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .slow is properly minimized when qname-minimization is on ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.4 flush
$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.4 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
NS boing.slow.
NS zoop.boing.slow.
NS ptang.zoop.boing.slow.
NS icky.ptang.zoop.boing.slow.
NS icky.icky.ptang.zoop.boing.slow.
A icky.icky.icky.ptang.zoop.boing.slow.
__EOF
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "query for .ip6.arpa succeds and skips on boundaries when qname-minimization is on ($n)"
ret=0
$CLEANQL
$RNDCCMD 10.53.0.4 flush
$DIG $DIGOPTS -x 2001:4f8::1 @10.53.0.4 > dig.out.test$n
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
NS 1.0.0.2.ip6.arpa.
NS 8.f.4.0.1.0.0.2.ip6.arpa.
NS 0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
NS 0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
NS 0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
PTR 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.
__EOF
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

0
bin/tests/system/testsock.pl Normal file → Executable file
View file

View file

@ -264,6 +264,8 @@ options {
preferred-glue <string>;
prefetch <integer> [ <integer> ];
provide-ixfr <boolean>;
qname-minimization <boolean>;
qname-minimization-strict <boolean>;
query-source ( ( [ address ] ( <ipv4_address> | * ) [ port (
<integer> | * ) ] ) | ( [ [ address ] ( <ipv4_address> | * ) ]
port ( <integer> | * ) ) ) [ dscp <integer> ];

View file

@ -105,6 +105,13 @@ typedef enum {
/* RESERVED ECS 0x2000 */
/* RESERVED TCPCLIENT 0x4000 */
#define DNS_FETCHOPT_NOCACHED 0x8000 /*%< Force cache update. */
#define DNS_FETCHOPT_QMINIMIZE 0x00010000 /*%< Use qname minimization. */
#define DNS_FETCHOPT_QMIN_STRICT 0x00020000 /*%< Do not work around servers that
return errors on non-empty
terminals. */
#define DNS_FETCHOPT_QMIN_SKIP_ON_IP6A 0x00040000 /*%< Skip some labels when
doing qname minimization on
ip6.arpa. */
/* Reserved in use by adb.c 0x00400000 */
#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
@ -267,7 +274,7 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
dns_rdatatype_t type,
const dns_name_t *domain, dns_rdataset_t *nameservers,
dns_forwarders_t *forwarders,
const isc_sockaddr_t *client, isc_uint16_t id,
const isc_sockaddr_t *client, dns_messageid_t id,
unsigned int options, unsigned int depth,
isc_counter_t *qc, isc_task_t *task,
isc_taskaction_t action, void *arg,

View file

@ -115,6 +115,8 @@ struct dns_view {
dns_order_t * order;
dns_fwdtable_t * fwdtable;
isc_boolean_t recursion;
isc_boolean_t qminimization;
isc_boolean_t qmin_strict;
isc_boolean_t auth_nxdomain;
isc_boolean_t use_glue_cache;
isc_boolean_t minimal_any;

View file

@ -258,8 +258,8 @@ struct fetchctx {
/*% Not locked. */
unsigned int magic;
dns_resolver_t * res;
dns_name_t name;
dns_rdatatype_t type;
dns_name_t fullname;
dns_rdatatype_t fulltype;
unsigned int options;
unsigned int bucketnum;
unsigned int dbucketnum;
@ -275,6 +275,7 @@ struct fetchctx {
isc_event_t control_event;
ISC_LINK(struct fetchctx) link;
ISC_LIST(dns_fetchevent_t) events;
/*% Locked by task event serialization. */
dns_name_t domain;
dns_rdataset_t nameservers;
@ -304,6 +305,11 @@ struct fetchctx {
isc_boolean_t ns_ttl_ok;
isc_uint32_t ns_ttl;
isc_counter_t * qc;
isc_boolean_t minimized;
unsigned int qmin_labels;
isc_boolean_t ip6arpaskip;
dns_name_t name;
dns_rdatatype_t type;
/*%
* The number of events we're waiting for.
@ -554,6 +560,11 @@ void dns_resolver_setfuzzing() {
}
#endif
static unsigned char ip6_arpa_data[] = "\003IP6\004ARPA";
static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
static const dns_name_t ip6_arpa =
DNS_NAME_INITABSOLUTE(ip6_arpa_data, ip6_arpa_offsets);
static void destroy(dns_resolver_t *res);
static void empty_bucket(dns_resolver_t *res);
static isc_result_t resquery_send(resquery_t *query);
@ -561,6 +572,7 @@ static void resquery_response(isc_task_t *task, isc_event_t *event);
static void resquery_connected(isc_task_t *task, isc_event_t *event);
static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying,
isc_boolean_t badcache);
void fctx_minimize_qname(fetchctx_t *fctx);
static void fctx_destroy(fetchctx_t *fctx);
static isc_boolean_t fctx_unlink(fetchctx_t *fctx);
static isc_result_t ncache_adderesult(dns_message_t *message,
@ -749,6 +761,12 @@ rctx_referral(respctx_t *rctx);
static isc_result_t
rctx_answer_none(respctx_t *rctx);
static void
rctx_follow_referral(respctx_t *rctx);
static isc_result_t
rctx_answer_minimized(respctx_t *rctx);
static void
rctx_nextserver(respctx_t *rctx, dns_adbaddrinfo_t *addrinfo,
isc_result_t result);
@ -4136,6 +4154,7 @@ fctx_destroy(fetchctx_t *fctx) {
dns_name_free(&fctx->domain, fctx->mctx);
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
dns_name_free(&fctx->fullname, fctx->mctx);
dns_name_free(&fctx->name, fctx->mctx);
dns_db_detach(&fctx->cache);
dns_adb_detach(&fctx->adb);
@ -4514,9 +4533,14 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
result = dns_name_dup(name, mctx, &fctx->name);
if (result != ISC_R_SUCCESS)
goto cleanup_info;
dns_name_init(&fctx->fullname, NULL);
result = dns_name_dup(name, mctx, &fctx->fullname);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
dns_name_init(&fctx->domain, NULL);
dns_rdataset_init(&fctx->nameservers);
fctx->fulltype = type;
fctx->type = type;
fctx->options = options;
/*
@ -4532,6 +4556,9 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
fctx->want_shutdown = ISC_FALSE;
fctx->cloned = ISC_FALSE;
fctx->depth = depth;
fctx->minimized = isc_boolean_false;
fctx->ip6arpaskip = isc_boolean_false;
fctx->qmin_labels = 1;
ISC_LIST_INIT(fctx->queries);
ISC_LIST_INIT(fctx->finds);
ISC_LIST_INIT(fctx->altfinds);
@ -4635,12 +4662,12 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
*/
result = dns_name_dup(fname, mctx, &fctx->domain);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
goto cleanup_fullname;
}
} else {
result = dns_name_dup(domain, mctx, &fctx->domain);
if (result != ISC_R_SUCCESS)
goto cleanup_name;
goto cleanup_fullname;
dns_rdataset_clone(nameservers, &fctx->nameservers);
fctx->ns_ttl = fctx->nameservers.ttl;
fctx->ns_ttl_ok = ISC_TRUE;
@ -4726,6 +4753,15 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
ISC_LINK_INIT(fctx, link);
fctx->magic = FCTX_MAGIC;
/*
* If qname minimization is enabled we need to trim
* the name in fctx to proper length.
*/
if (options & DNS_FETCHOPT_QMINIMIZE) {
fctx->ip6arpaskip = (options & DNS_FETCHOPT_QMIN_SKIP_ON_IP6A) && dns_name_issubdomain(&fctx->name, &ip6_arpa);
fctx_minimize_qname(fctx);
}
ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
LOCK(&res->nlock);
@ -4754,6 +4790,9 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
if (dns_rdataset_isassociated(&fctx->nameservers))
dns_rdataset_disassociate(&fctx->nameservers);
cleanup_fullname:
dns_name_free(&fctx->fullname, mctx);
cleanup_name:
dns_name_free(&fctx->name, mctx);
@ -5636,6 +5675,8 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
unsigned int valoptions = 0;
isc_boolean_t checknta = ISC_TRUE;
FCTXTRACE("cache_name");
/*
* The appropriate bucket lock must be held.
*/
@ -6459,12 +6500,10 @@ check_related(void *arg, const dns_name_t *addname, dns_rdatatype_t type) {
#ifndef CHECK_FOR_GLUE_IN_ANSWER
#define CHECK_FOR_GLUE_IN_ANSWER 0
#endif
#if CHECK_FOR_GLUE_IN_ANSWER
static isc_result_t
check_answer(void *arg, const dns_name_t *addname, dns_rdatatype_t type) {
return (check_section(arg, addname, type, DNS_SECTION_ANSWER));
}
#endif
static isc_boolean_t
is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
@ -7752,7 +7791,12 @@ rctx_answer(respctx_t *rctx) {
fetchctx_t *fctx = rctx->fctx;
resquery_t *query = rctx->query;
if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
if (fctx->minimized) {
result = rctx_answer_minimized(rctx);
if (result != ISC_R_SUCCESS) {
FCTXTRACE3("rctx_answer_minimized", result);
}
} else if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
ISFORWARDER(query->addrinfo))
{
result = rctx_answer_positive(rctx);
@ -8475,6 +8519,16 @@ rctx_answer_none(respctx_t *rctx) {
return (rctx->result);
}
/*
* If we're doing qname minimization this is an empty non-terminal, add
* the next label to query and restart it.
*/
if (fctx->minimized &&
(fctx->rmessage->rcode == dns_rcode_noerror ||
!(fctx->options & DNS_FETCHOPT_QMIN_STRICT))) {
return rctx_answer_minimized(rctx);
}
/*
* Since we're not doing a referral, we don't want to cache any
* NS RRs we may have found.
@ -8842,6 +8896,10 @@ rctx_referral(respctx_t *rctx) {
check_answer, fctx);
}
#endif
if (fctx->minimized) {
(void)dns_rdataset_additionaldata(rctx->ns_rdataset,
check_answer, fctx);
}
fctx->attributes &= ~FCTX_ATTR_GLUING;
/*
@ -8887,6 +8945,39 @@ rctx_referral(respctx_t *rctx) {
log_ns_ttl(fctx, "DELEGATION");
rctx->result = DNS_R_DELEGATION;
rctx_follow_referral(rctx);
if (rctx->fctx->minimized) {
/*
* Reset the value
*/
fctx->qmin_labels = 1;
fctx_minimize_qname(rctx->fctx);
}
return (ISC_R_COMPLETE);
}
/*
* rctx_answer_minimized():
* Handles a positive response to a qname-minimized query.
*/
static isc_result_t
rctx_answer_minimized(respctx_t *rctx) {
fetchctx_t *fctx = rctx->fctx;
FCTXTRACE("rctx_answer_minimized");
if (dns_rdataset_isassociated(&fctx->nameservers)) {
dns_rdataset_disassociate(&fctx->nameservers);
}
rctx->result = DNS_R_DELEGATION;
rctx_follow_referral(rctx);
fctx->qmin_labels++;
fctx_minimize_qname(rctx->fctx);
return (ISC_R_SUCCESS);
}
static void
rctx_follow_referral(respctx_t *rctx) {
/*
* Reinitialize 'rctx' to prepare for following the delegation:
* set the get_nameservers and next_server flags appropriately and
@ -8903,8 +8994,6 @@ rctx_referral(respctx_t *rctx) {
rctx->fctx->neterr = 0;
rctx->fctx->badresp = 0;
rctx->fctx->adberr = 0;
return (ISC_R_COMPLETE);
}
/*
@ -10138,9 +10227,9 @@ fctx_match(fetchctx_t *fctx, const dns_name_t *name, dns_rdatatype_t type,
ISC_LIST_EMPTY(fctx->events))
return (ISC_FALSE);
if (fctx->type != type || fctx->options != options)
if (fctx->fulltype != type || fctx->options != options)
return (ISC_FALSE);
return (dns_name_equal(&fctx->name, name));
return (dns_name_equal(&fctx->fullname, name));
}
static inline void
@ -10164,6 +10253,57 @@ log_fetch(const dns_name_t *name, dns_rdatatype_t type) {
"fetch: %s/%s", namebuf, typebuf);
}
void
fctx_minimize_qname(fetchctx_t *fctx) {
/* XXXWPK TODO we should update info to show that this query is minimized */
unsigned int dlabels, nlabels;
dlabels = dns_name_countlabels(&fctx->domain);
nlabels = dns_name_countlabels(&fctx->fullname);
dns_name_free(&fctx->name, fctx->mctx);
dns_name_init(&fctx->name, NULL);
if (fctx->ip6arpaskip) {
/*
* For ip6.arpa we want to skip some of the labels, with boundaries
* at /16, /32, /48, /56, /64 and /128
* in 'label count' terms that's equal to
* 7 11 15 17 19 35
* We fix fctx->qmin_labels to point to the nearest boundary
*/
if (dlabels + fctx->qmin_labels < 7) {
fctx->qmin_labels = 7 - dlabels;
} else if (dlabels + fctx->qmin_labels < 11) {
fctx->qmin_labels = 11 - dlabels;
} else if (dlabels + fctx->qmin_labels < 15) {
fctx->qmin_labels = 15 - dlabels;
} else if (dlabels + fctx->qmin_labels < 17) {
fctx->qmin_labels = 17 - dlabels;
} else if (dlabels + fctx->qmin_labels < 19) {
fctx->qmin_labels = 19 - dlabels;
} else if (dlabels + fctx->qmin_labels > 19) {
fctx->qmin_labels = 35 - dlabels;
}
}
if (dlabels + fctx->qmin_labels < nlabels) {
/*
* We want to query for [qmin_labels from fctx->fullname] + fctx->domain
*/
dns_fixedname_t fname;
dns_fixedname_init(&fname);
dns_name_split(&fctx->fullname,
dlabels + fctx->qmin_labels,
NULL, dns_fixedname_name(&fname));
dns_name_dup(dns_fixedname_name(&fname), fctx->mctx, &fctx->name);
fctx->type = dns_rdatatype_ns;
fctx->minimized = isc_boolean_true;
} else {
/* Minimization is done, we'll ask for whole qname */
fctx->type = fctx->fulltype;
dns_name_dup(&fctx->fullname, fctx->mctx, &fctx->name);
fctx->minimized = isc_boolean_false;
}
}
isc_result_t
dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
dns_rdatatype_t type,

View file

@ -182,6 +182,8 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
* Initialize configuration data with default values.
*/
view->recursion = ISC_TRUE;
view->qminimization = ISC_FALSE;
view->qmin_strict = ISC_FALSE;
view->auth_nxdomain = ISC_FALSE; /* Was true in BIND 8 */
view->enablednssec = ISC_TRUE;
view->enablevalidation = ISC_TRUE;

View file

@ -1937,6 +1937,8 @@ view_clauses[] = {
{ "preferred-glue", &cfg_type_astring, 0 },
{ "prefetch", &cfg_type_prefetch, 0 },
{ "provide-ixfr", &cfg_type_boolean, 0 },
{ "qname-minimization", &cfg_type_boolean, 0 },
{ "qname-minimization-strict", &cfg_type_boolean, 0 },
/*
* Note that the query-source option syntax is different
* from the other -source options.

View file

@ -11106,6 +11106,15 @@ ns_query_start(ns_client_t *client) {
} else if (!client->view->enablevalidation)
client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
if (client->view->qminimization) {
client->query.fetchoptions |= DNS_FETCHOPT_QMINIMIZE |
DNS_FETCHOPT_QMIN_SKIP_ON_IP6A;
if (client->view->qmin_strict) {
client->query.fetchoptions |=
DNS_FETCHOPT_QMIN_STRICT;
}
}
/*
* Allow glue NS records to be added to the authority section
* if the answer is secure.

View file

@ -1729,6 +1729,15 @@
./bin/tests/system/pkcs11ssl/setup.sh SH 2014,2016,2018
./bin/tests/system/pkcs11ssl/tests.sh SH 2014,2016,2018
./bin/tests/system/pkcs11ssl/usepkcs11 X 2014,2018
./bin/tests/system/qname-minimization/ans2/ans.py PYTHON 2018
./bin/tests/system/qname-minimization/clean.sh SH 2018
./bin/tests/system/qname-minimization/ns1/named.conf.in CONF-C 2018
./bin/tests/system/qname-minimization/ns1/root.db ZONE 2018
./bin/tests/system/qname-minimization/ns3/named.conf.in CONF-C 2018
./bin/tests/system/qname-minimization/ns4/named.conf.in CONF-C 2018
./bin/tests/system/qname-minimization/ns5/named.conf.in CONF-C 2018
./bin/tests/system/qname-minimization/setup.sh SH 2018
./bin/tests/system/qname-minimization/tests.sh SH 2018
./bin/tests/system/reclimit/README TXT.BRIEF 2014,2016,2017,2018
./bin/tests/system/reclimit/ans2/ans.pl PERL 2014,2015,2016,2017,2018
./bin/tests/system/reclimit/ans7/ans.pl PERL 2014,2016,2018