Handle RRSIG signer case consistently

3329.	[bug]	Handle RRSIG signer-name case consistently: We
		generate RRSIG records with the signer-name in
		lower case.  We accept them with any case, but if
		they fail to validate, we try again in lower case.
		[RT #27451]
This commit is contained in:
Evan Hunt
2012-05-17 11:39:23 -07:00
parent 511687b15b
commit c92cfa7eae
15 changed files with 259 additions and 26 deletions

View File

@@ -1,3 +1,9 @@
3329. [bug] Handle RRSIG signer-name case consistently: We
generate RRSIG records with the signer-name in
lower case. We accept them with any case, but if
they fail to validate, we try again in lower case.
[RT #27451]
--- 9.6-ESV-R7 released ---
3318. [tuning] Reduce the amount of work performed while holding a

View File

@@ -84,16 +84,19 @@ static const char *nsstats_desc[dns_nsstatscounter_max];
static const char *resstats_desc[dns_resstatscounter_max];
static const char *zonestats_desc[dns_zonestatscounter_max];
static const char *sockstats_desc[isc_sockstatscounter_max];
static const char *dnssecstats_desc[dns_dnssecstats_max];
#ifdef HAVE_LIBXML2
static const char *nsstats_xmldesc[dns_nsstatscounter_max];
static const char *resstats_xmldesc[dns_resstatscounter_max];
static const char *zonestats_xmldesc[dns_zonestatscounter_max];
static const char *sockstats_xmldesc[isc_sockstatscounter_max];
static const char *dnssecstats_xmldesc[dns_dnssecstats_max];
#else
#define nsstats_xmldesc NULL
#define resstats_xmldesc NULL
#define zonestats_xmldesc NULL
#define sockstats_xmldesc NULL
#define dnssecstats_xmldesc NULL
#endif /* HAVE_LIBXML2 */
#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
@@ -107,6 +110,7 @@ static int nsstats_index[dns_nsstatscounter_max];
static int resstats_index[dns_resstatscounter_max];
static int zonestats_index[dns_zonestatscounter_max];
static int sockstats_index[isc_sockstatscounter_max];
static int dnssecstats_index[dns_dnssecstats_max];
static inline void
set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
@@ -408,6 +412,33 @@ init_desc(void) {
"FDwatchRecvErr");
INSIST(i == isc_sockstatscounter_max);
/* Initialize DNSSEC statistics */
for (i = 0; i < dns_dnssecstats_max; i++)
dnssecstats_desc[i] = NULL;
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_dnssecstats_max; i++)
dnssecstats_xmldesc[i] = NULL;
#endif
#define SET_DNSSECSTATDESC(counterid, desc, xmldesc) \
do { \
set_desc(dns_dnssecstats_ ## counterid, \
dns_dnssecstats_max, \
desc, dnssecstats_desc,\
xmldesc, dnssecstats_xmldesc); \
dnssecstats_index[i++] = dns_dnssecstats_ ## counterid; \
} while (0)
i = 0;
SET_DNSSECSTATDESC(asis, "dnssec validation success with signer "
"\"as is\"", "DNSSECasis");
SET_DNSSECSTATDESC(downcase, "dnssec validation success with signer "
"lower cased", "DNSSECdowncase");
SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature",
"DNSSECwild");
SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail");
INSIST(i == dns_dnssecstats_max);
/* Sanity check */
for (i = 0; i < dns_nsstatscounter_max; i++)
INSIST(nsstats_desc[i] != NULL);
@@ -417,6 +448,8 @@ init_desc(void) {
INSIST(zonestats_desc[i] != NULL);
for (i = 0; i < isc_sockstatscounter_max; i++)
INSIST(sockstats_desc[i] != NULL);
for (i = 0; i < dns_dnssecstats_max; i++)
INSIST(dnssecstats_desc[i] != NULL);
#ifdef HAVE_LIBXML2
for (i = 0; i < dns_nsstatscounter_max; i++)
INSIST(nsstats_xmldesc[i] != NULL);
@@ -426,6 +459,8 @@ init_desc(void) {
INSIST(zonestats_xmldesc[i] != NULL);
for (i = 0; i < isc_sockstatscounter_max; i++)
INSIST(sockstats_xmldesc[i] != NULL);
for (i = 0; i < dns_dnssecstats_max; i++)
INSIST(dnssecstats_xmldesc[i] != NULL);
#endif
}

View File

@@ -44,3 +44,4 @@ rm -f signer/example.db
rm -f ns2/algroll.db
rm -f signer/example.db.after signer/example.db.before
rm -f signer/example.db.changed
rm -f ns3/lower.example.db ns3/upper.example.db ns3/upper.example.db.lower

View File

@@ -119,3 +119,9 @@ ns.insecure.below-cname A 10.53.0.3
secure.below-cname NS ns.secure.below-cname
ns.secure.below-cname A 10.53.0.3
upper NS ns.upper
ns.upper A 10.53.0.3
LOWER NS NS.LOWER
NS.LOWER A 10.53.0.3

View File

@@ -32,7 +32,7 @@ zonefile=example.db
for subdomain in secure bogus dynamic keyless nsec3 optout nsec3-unknown \
optout-unknown multiple rsasha256 rsasha512 update-nsec3 \
secure.below-cname expired
secure.below-cname expired upper lower
do
cp ../ns3/keyset-$subdomain.example. .
done

View File

@@ -0,0 +1,26 @@
; Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; copyright notice and this permission notice appear in all copies.
;
; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
; $Id: lower.example.db.in,v 1.1.2.1 2012/01/17 08:31:00 marka Exp $
$TTL 300 ; 5 minutes
@ IN SOA MNAME1. . (
2012042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
@ NS NS
NS A 10.53.0.3

View File

@@ -188,4 +188,14 @@ zone "secure.below-cname.example" {
file "secure.below-cname.example.db.signed";
};
zone "upper.example" {
type master;
file "upper.example.db.signed";
};
zone "LOWER.EXAMPLE" {
type master;
file "lower.example.db.signed";
};
include "trusted.conf";

View File

@@ -283,3 +283,31 @@ zonefile=secure.below-cname.example.db
keyname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -n zone $zone`
cat $infile $keyname.key >$zonefile
$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
#
# A zone where the signer's name has been forced to uppercase.
#
zone="upper.example."
infile="upper.example.db.in"
zonefile="upper.example.db"
lower="upper.example.db.lower"
signedfile="upper.example.db.signed"
kskname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 $zone`
zskname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -f KSK $zone`
cat $infile $kskname.key $zskname.key > $zonefile
$SIGNER -P -r $RANDFILE -o $zone -f $lower $zonefile > /dev/null 2>&1
$CHECKZONE -D upper.example $lower 2>&- | \
awk '$4 == "RRSIG" {$12 = toupper($12); print; next} { print }' > $signedfile
#
# Check that the signer's name is in lower case when zone name is in
# upper case.
#
zone="LOWER.EXAMPLE."
infile="lower.example.db.in"
zonefile="lower.example.db"
signedfile="lower.example.db.signed"
kskname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 $zone`
zskname=`$KEYGEN -r $RANDFILE -a RSASHA1 -b 1024 -f KSK $zone`
cat $infile $kskname.key $zskname.key > $zonefile
$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1

View File

@@ -0,0 +1,26 @@
; Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; copyright notice and this permission notice appear in all copies.
;
; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
; $Id: upper.example.db.in,v 1.1.2.1 2012/01/17 08:31:00 marka Exp $
$TTL 300 ; 5 minutes
@ IN SOA mname1. . (
2012042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
@ NS ns
ns A 10.53.0.3

View File

@@ -1129,5 +1129,25 @@ n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:testing legacy upper case signer name validation ($n)"
ret=0
$DIG +tcp +dnssec -p 5300 +noadd +noauth soa upper.example @10.53.0.4 \
> dig.out.ns4.test$n 2>&1
grep 'flags:.* ad;' dig.out.ns4.test$n >/dev/null || ret=1
grep 'RRSIG.*SOA.* UPPER\.EXAMPLE\. ' dig.out.ns4.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:testing that we lower case signer name ($n)"
ret=0
$DIG +tcp +dnssec -p 5300 +noadd +noauth soa LOWER.EXAMPLE @10.53.0.4 \
> dig.out.ns4.test$n 2>&1
grep 'flags:.* ad;' dig.out.ns4.test$n >/dev/null || ret=1
grep 'RRSIG.*SOA.* lower\.example\. ' dig.out.ns4.test$n > /dev/null || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status

View File

@@ -35,16 +35,20 @@
#include <dns/dnssec.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/log.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
#include <dns/stats.h>
#include <dns/tsig.h> /* for DNS_TSIG_FUDGE */
#include <dst/result.h>
LIBDNS_EXTERNAL_DATA isc_stats_t *dns_dnssec_stats;
#define is_response(msg) (msg->flags & DNS_MESSAGEFLAG_QR)
#define RETERR(x) do { \
@@ -74,6 +78,12 @@ digest_callback(void *arg, isc_region_t *data) {
return (dst_context_adddata(ctx, data));
}
static inline void
inc_stat(isc_statscounter_t counter) {
if (dns_dnssec_stats != NULL)
isc_stats_increment(dns_dnssec_stats, counter);
}
/*
* Make qsort happy.
*/
@@ -150,7 +160,9 @@ dns_dnssec_keyfromrdata(dns_name_t *name, dns_rdata_t *rdata, isc_mem_t *mctx,
}
static isc_result_t
digest_sig(dst_context_t *ctx, dns_rdata_t *sigrdata, dns_rdata_rrsig_t *sig) {
digest_sig(dst_context_t *ctx, isc_boolean_t downcase, dns_rdata_t *sigrdata,
dns_rdata_rrsig_t *rrsig)
{
isc_region_t r;
isc_result_t ret;
dns_fixedname_t fname;
@@ -162,11 +174,16 @@ digest_sig(dst_context_t *ctx, dns_rdata_t *sigrdata, dns_rdata_rrsig_t *sig) {
ret = dst_context_adddata(ctx, &r);
if (ret != ISC_R_SUCCESS)
return (ret);
dns_fixedname_init(&fname);
RUNTIME_CHECK(dns_name_downcase(&sig->signer,
dns_fixedname_name(&fname), NULL)
== ISC_R_SUCCESS);
dns_name_toregion(dns_fixedname_name(&fname), &r);
if (downcase) {
dns_fixedname_init(&fname);
RUNTIME_CHECK(dns_name_downcase(&rrsig->signer,
dns_fixedname_name(&fname),
NULL) == ISC_R_SUCCESS);
dns_name_toregion(dns_fixedname_name(&fname), &r);
} else
dns_name_toregion(&rrsig->signer, &r);
return (dst_context_adddata(ctx, &r));
}
@@ -188,6 +205,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
isc_uint32_t flags;
unsigned int sigsize;
dns_fixedname_t fnewname;
dns_fixedname_t fsigner;
REQUIRE(name != NULL);
REQUIRE(dns_name_countlabels(name) <= 255);
@@ -215,8 +233,14 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
sig.common.rdtype = dns_rdatatype_rrsig;
ISC_LINK_INIT(&sig.common, link);
/*
* Downcase signer.
*/
dns_name_init(&sig.signer, NULL);
dns_name_clone(dst_key_name(key), &sig.signer);
dns_fixedname_init(&fsigner);
RUNTIME_CHECK(dns_name_downcase(dst_key_name(key),
dns_fixedname_name(&fsigner), NULL) == ISC_R_SUCCESS);
dns_name_clone(dns_fixedname_name(&fsigner), &sig.signer);
sig.covered = set->type;
sig.algorithm = dst_key_alg(key);
@@ -256,7 +280,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Digest the SIG rdata.
*/
ret = digest_sig(ctx, &tmpsigrdata, &sig);
ret = digest_sig(ctx, ISC_FALSE, &tmpsigrdata, &sig);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
@@ -329,7 +353,7 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
memcpy(sig.signature, r.base, sig.siglen);
ret = dns_rdata_fromstruct(sigrdata, sig.common.rdclass,
sig.common.rdtype, &sig, buffer);
sig.common.rdtype, &sig, buffer);
cleanup_array:
isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
@@ -360,6 +384,7 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
dst_context_t *ctx = NULL;
int labels = 0;
isc_uint32_t flags;
isc_boolean_t downcase = ISC_FALSE;
REQUIRE(name != NULL);
REQUIRE(set != NULL);
@@ -374,8 +399,10 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
if (set->type != sig.covered)
return (DNS_R_SIGINVALID);
if (isc_serial_lt(sig.timeexpire, sig.timesigned))
if (isc_serial_lt(sig.timeexpire, sig.timesigned)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
}
if (!ignoretime) {
isc_stdtime_get(&now);
@@ -383,10 +410,13 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Is SIG temporally valid?
*/
if (isc_serial_lt((isc_uint32_t)now, sig.timesigned))
if (isc_serial_lt((isc_uint32_t)now, sig.timesigned)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGFUTURE);
else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now))
} else if (isc_serial_lt(sig.timeexpire, (isc_uint32_t)now)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGEXPIRED);
}
}
/*
@@ -397,16 +427,22 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
case dns_rdatatype_ns:
case dns_rdatatype_soa:
case dns_rdatatype_dnskey:
if (!dns_name_equal(name, &sig.signer))
if (!dns_name_equal(name, &sig.signer)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
}
break;
case dns_rdatatype_ds:
if (dns_name_equal(name, &sig.signer))
if (dns_name_equal(name, &sig.signer)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
}
/* FALLTHROUGH */
default:
if (!dns_name_issubdomain(name, &sig.signer))
if (!dns_name_issubdomain(name, &sig.signer)) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_SIGINVALID);
}
break;
}
@@ -414,11 +450,16 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
* Is the key allowed to sign data?
*/
flags = dst_key_flags(key);
if (flags & DNS_KEYTYPE_NOAUTH)
if (flags & DNS_KEYTYPE_NOAUTH) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_KEYUNAUTHORIZED);
if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE)
}
if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) {
inc_stat(dns_dnssecstats_fail);
return (DNS_R_KEYUNAUTHORIZED);
}
again:
ret = dst_context_create(key, mctx, &ctx);
if (ret != ISC_R_SUCCESS)
goto cleanup_struct;
@@ -426,7 +467,7 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
/*
* Digest the SIG rdata (not including the signature).
*/
ret = digest_sig(ctx, sigrdata, &sig);
ret = digest_sig(ctx, downcase, sigrdata, &sig);
if (ret != ISC_R_SUCCESS)
goto cleanup_context;
@@ -505,21 +546,40 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
r.base = sig.signature;
r.length = sig.siglen;
ret = dst_context_verify(ctx, &r);
if (ret == DST_R_VERIFYFAILURE)
ret = DNS_R_SIGINVALID;
if (ret == ISC_R_SUCCESS && downcase) {
char namebuf[DNS_NAME_FORMATSIZE];
dns_name_format(&sig.signer, namebuf, sizeof(namebuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
"sucessfully validated after lower casing "
"signer '%s'", namebuf);
inc_stat(dns_dnssecstats_downcase);
} else if (ret == ISC_R_SUCCESS)
inc_stat(dns_dnssecstats_asis);
cleanup_array:
isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t));
cleanup_context:
dst_context_destroy(&ctx);
if (ret == DST_R_VERIFYFAILURE && !downcase) {
downcase = ISC_TRUE;
goto again;
}
cleanup_struct:
dns_rdata_freestruct(&sig);
if (ret == DST_R_VERIFYFAILURE)
ret = DNS_R_SIGINVALID;
if (ret != ISC_R_SUCCESS)
inc_stat(dns_dnssecstats_fail);
if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) {
if (wild != NULL)
RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname,
dns_fixedname_name(&fnewname),
wild, NULL) == ISC_R_SUCCESS);
inc_stat(dns_dnssecstats_wildcard);
ret = DNS_R_FROMWILDCARD;
}
return (ret);

View File

@@ -24,6 +24,7 @@
#include <isc/lang.h>
#include <isc/stdtime.h>
#include <isc/stats.h>
#include <dns/types.h>
@@ -31,6 +32,8 @@
ISC_LANG_BEGINDECLS
LIBDNS_EXTERNAL_DATA extern isc_stats_t *dns_dnssec_stats;
/*%< Maximum number of keys supported in a zone. */
#define DNS_MAXZONEKEYS 32
@@ -60,8 +63,8 @@ dns_dnssec_sign(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
isc_stdtime_t *inception, isc_stdtime_t *expire,
isc_mem_t *mctx, isc_buffer_t *buffer, dns_rdata_t *sigrdata);
/*%<
* Generates a SIG record covering this rdataset. This has no effect
* on existing SIG records.
* Generates a RRSIG record covering this rdataset. This has no effect
* on existing RRSIG records.
*
* Requires:
*\li 'name' (the owner name of the record) is a valid name
@@ -94,9 +97,9 @@ dns_dnssec_verify2(dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
isc_boolean_t ignoretime, isc_mem_t *mctx,
dns_rdata_t *sigrdata, dns_name_t *wild);
/*%<
* Verifies the SIG record covering this rdataset signed by a specific
* key. This does not determine if the key's owner is authorized to
* sign this record, as this requires a resolver or database.
* Verifies the RRSIG record covering this rdataset signed by a specific
* key. This does not determine if the key's owner is authorized to sign
* this record, as this requires a resolver or database.
* If 'ignoretime' is ISC_TRUE, temporal validity will not be checked.
*
* Requires:

View File

@@ -73,6 +73,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
#define DNS_LOGMODULE_HINTS (&dns_modules[24])
#define DNS_LOGMODULE_ACACHE (&dns_modules[25])
#define DNS_LOGMODULE_DLZ (&dns_modules[26])
#define DNS_LOGMODULE_DNSSEC (&dns_modules[27])
ISC_LANG_BEGINDECLS

View File

@@ -64,6 +64,16 @@ enum {
dns_resstatscounter_max = 30,
/*
* DNSSEC stats.
*/
dns_dnssecstats_asis = 0,
dns_dnssecstats_downcase = 1,
dns_dnssecstats_wildcard = 2,
dns_dnssecstats_fail = 3,
dns_dnssecstats_max = 4,
/*%
* Zone statistics counters.
*/

View File

@@ -79,6 +79,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = {
{ "dns/hints", 0 },
{ "dns/acache", 0 },
{ "dns/dlz", 0 },
{ "dns/dnssec", 0 },
{ NULL, 0 }
};