From f05089ea5c3644cd82bdb596cb10d95d48e5e53d Mon Sep 17 00:00:00 2001 From: Vernon Schryver Date: Fri, 1 Jun 2012 01:03:43 +0000 Subject: [PATCH] For rt26172: Add - optional "recursive-only yes|no" to the response-policy statement - optional max-policy-ttl to limit the lies that "recursive-only no" can introduce into resolvers' caches - test that queries with RD=0 are not rewritten by default - performance smoke test Change encoding of PASSTHRU action to "rpz-passthru". (The old encoding is still accepted.) Fix rt26180 assert botch in zone_findrdataset() in this branch as well. Fix missing signatures on NOERROR results despite RPZ hits when there are signatures and the client asks for DNSSEC, --- CHANGES | 15 ++ bin/named/query.c | 154 +++++++++++--- bin/named/server.c | 104 ++++++---- bin/tests/system/rpz/clean.sh | 10 +- bin/tests/system/rpz/ns1/root.db | 8 +- bin/tests/system/rpz/ns2/base-tld2s.db | 31 +++ bin/tests/system/rpz/ns2/named.conf | 4 +- bin/tests/system/rpz/ns2/tld2.db | 11 +- bin/tests/system/rpz/ns3/base.db | 13 +- bin/tests/system/rpz/ns3/crash2 | 4 +- bin/tests/system/rpz/ns3/named.conf | 13 +- bin/tests/system/rpz/ns4/named.conf | 3 +- bin/tests/system/rpz/ns4/tld4.db | 4 +- bin/tests/system/rpz/ns5/hints | 19 ++ bin/tests/system/rpz/ns5/named.conf | 51 +++++ bin/tests/system/rpz/qperf.sh | 27 +++ bin/tests/system/rpz/rpz.c | 10 +- bin/tests/system/rpz/setup.sh | 96 ++++++++- bin/tests/system/rpz/test1 | 38 ++-- bin/tests/system/rpz/test2 | 17 +- bin/tests/system/rpz/test5 | 27 ++- bin/tests/system/rpz/tests.sh | 270 ++++++++++++++++++------- doc/arm/Bv9ARM-book.xml | 167 +++++++++------ lib/dns/db.c | 11 +- lib/dns/include/dns/db.h | 10 +- lib/dns/include/dns/rpz.h | 13 +- lib/dns/include/dns/view.h | 2 + lib/dns/rbtdb.c | 12 +- lib/dns/rpz.c | 129 +++++++----- lib/dns/view.c | 2 + lib/isccfg/namedconf.c | 240 +++++++++++++++------- util/copyrights | 6 +- 32 files changed, 1104 insertions(+), 417 deletions(-) create mode 100644 bin/tests/system/rpz/ns2/base-tld2s.db create mode 100644 bin/tests/system/rpz/ns5/hints create mode 100644 bin/tests/system/rpz/ns5/named.conf create mode 100644 bin/tests/system/rpz/qperf.sh diff --git a/CHANGES b/CHANGES index e158fbbba4..5e4a50504e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,18 @@ +3330. [func] Fix missing signatures on NOERROR results despite + RPZ rewriting. Also + - add optional "recursive-only yes|no" to the + response-policy statement + - add optional "max-policy-ttl" to the response-policy + statement to limit the false data that + "recursive-only no" can introduce into + resolvers' caches + - add a RPZ performance test to bin/tests/system/rpz + when queryperf is available. + - the encoding of PASSTHRU action to "rpz-passthru". + (The old encoding is still accepted.) + [RT #26172] + + 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 diff --git a/bin/named/query.c b/bin/named/query.c index abf28eee93..bb243315e0 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -3845,6 +3845,13 @@ rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_disassociate(*rdatasetp); } +static void +rpz_match_clear(dns_rpz_st_t *st) +{ + rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset); + st->m.version = NULL; +} + static inline isc_result_t rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp) @@ -3864,10 +3871,9 @@ static void rpz_st_clear(ns_client_t *client) { dns_rpz_st_t *st = client->query.rpz_st; - rpz_clean(&st->m.zone, &st->m.db, &st->m.node, NULL); - st->m.version = NULL; if (st->m.rdataset != NULL) query_putrdataset(client, &st->m.rdataset); + rpz_match_clear(st); rpz_clean(NULL, &st->r.db, NULL, NULL); if (st->r.ns_rdataset != NULL) @@ -4023,6 +4029,9 @@ rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { + if (!RECURSIONOK(client) && rpz->recursive_only) + continue; + /* * Do not check policy zones that cannot replace a policy * already known to match. @@ -4051,9 +4060,8 @@ rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, * hit, if any. Note the domain name and quality of the * best hit. */ - (void)dns_db_rpz_findips(rpz, rpz_type, zone, db, version, - rdataset, st, - client->query.rpz_st->qname); + dns_db_rpz_findips(rpz, rpz_type, zone, db, version, + rdataset, st, client->query.rpz_st->qname); rpz_clean(&zone, &db, NULL, NULL); } return (ISC_R_SUCCESS); @@ -4158,8 +4166,8 @@ rpz_rewrite_rrsets(ns_client_t *client, dns_rpz_type_t rpz_type, */ static isc_result_t rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, - dns_name_t *sname, dns_rpz_type_t rpz_type, dns_zone_t **zonep, - dns_db_t **dbp, dns_dbversion_t **versionp, + dns_name_t *sname, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, + dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp, dns_rpz_policy_t *policyp) { @@ -4204,7 +4212,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, if (result != ISC_R_SUCCESS) { dns_db_detachnode(*dbp, nodep); rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, - qnamef, "allrdatasets()", result); + qnamef, "allrdatasets() ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); } @@ -4221,7 +4229,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, if (result != ISC_R_SUCCESS) { if (result != ISC_R_NOMORE) { rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, - rpz_type, qnamef, "rdatasetiter", + rpz_type, qnamef, "rdatasetiter ", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); @@ -4249,7 +4257,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, if ((*rdatasetp)->type != dns_rdatatype_cname) { policy = DNS_RPZ_POLICY_RECORD; } else { - policy = dns_rpz_decode_cname(*rdatasetp, sname); + policy = dns_rpz_decode_cname(rpz, *rdatasetp, sname); if ((policy == DNS_RPZ_POLICY_RECORD || policy == DNS_RPZ_POLICY_WILDCNAME) && qtype != dns_rdatatype_cname && @@ -4320,6 +4328,9 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { + if (!RECURSIONOK(client) && rpz->recursive_only) + continue; + /* * Do not check policy zones that cannot replace a policy * already known to match. @@ -4365,11 +4376,11 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, } /* - * See if the policy record exists. + * See if the policy record exists and get its policy. */ - result = rpz_find(client, qtype, rpz_qname, qname, rpz_type, - &zone, &db, &version, &node, rdatasetp, - &policy); + result = rpz_find(client, qtype, rpz_qname, qname, rpz, + rpz_type, &zone, &db, &version, &node, + rdatasetp, &policy); switch (result) { case DNS_R_NXDOMAIN: case DNS_R_EMPTYNAME: @@ -4405,8 +4416,7 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, continue; } - rpz_clean(&st->m.zone, &st->m.db, &st->m.node, - &st->m.rdataset); + rpz_match_clear(st); st->m.rpz = rpz; st->m.type = rpz_type; st->m.prefix = 0; @@ -4420,9 +4430,11 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, trdataset = st->m.rdataset; st->m.rdataset = *rdatasetp; *rdatasetp = trdataset; - st->m.ttl = st->m.rdataset->ttl; + st->m.ttl = ISC_MIN(st->m.rdataset->ttl, + rpz->max_policy_ttl); } else { - st->m.ttl = DNS_RPZ_TTL_DEFAULT; + st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, + rpz->max_policy_ttl); } st->m.node = node; node = NULL; @@ -4517,13 +4529,13 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, case DNS_R_BROKENCHAIN: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, DNS_RPZ_TYPE_QNAME, client->query.qname, - "stop on qresult in rpz_rewrite()", + "stop on qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); default: rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, DNS_RPZ_TYPE_QNAME, client->query.qname, - "stop on unrecognized qresult in rpz_rewrite()", + "stop on unrecognized qresult in rpz_rewrite() ", qresult); return (ISC_R_SUCCESS); } @@ -4702,10 +4714,11 @@ cleanup: if (st->m.policy == DNS_RPZ_POLICY_MISS || st->m.policy == DNS_RPZ_POLICY_PASSTHRU || st->m.policy == DNS_RPZ_POLICY_ERROR) { - if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU) + if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU && + result != DNS_R_DELEGATION) rpz_log_rewrite(client, "", st->m.policy, st->m.type, st->qname); - rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset); + rpz_match_clear(st); } if (st->m.policy == DNS_RPZ_POLICY_ERROR) { st->m.type = DNS_RPZ_TYPE_BAD; @@ -4718,6 +4731,64 @@ cleanup: return (result); } +/* + * See if response policy zone rewriting is allowed a lack of interest + * by the client in DNSSEC or a lack of signatures. + */ +static isc_boolean_t +rpz_ck_dnssec(ns_client_t *client, isc_result_t result, + dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) +{ + dns_fixedname_t fixed; + dns_name_t *found; + dns_rdataset_t trdataset; + dns_rdatatype_t type; + + if (client->view->rpz_break_dnssec) + return (ISC_TRUE); + /* + * sigrdataset == NULL if and only !WANTDNSSEC(client) + */ + if (sigrdataset == NULL) + return (ISC_TRUE); + if (dns_rdataset_isassociated(sigrdataset)) + return (ISC_FALSE); + + /* + * We are happy to rewrite nothing. + */ + if (rdataset == NULL || !dns_rdataset_isassociated(rdataset)) + return (ISC_TRUE); + /* + * Do not rewrite if there is any sign of signatures. + */ + if (rdataset->type == dns_rdatatype_nsec || + rdataset->type == dns_rdatatype_nsec3 || + rdataset->type == dns_rdatatype_rrsig) + return (ISC_FALSE); + + /* + * Look for a signature in a negative cache rdataset. + */ + if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) == 0) + return (ISC_TRUE); + dns_fixedname_init(&fixed); + found = dns_fixedname_name(&fixed); + dns_rdataset_init(&trdataset); + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) { + dns_ncache_current(rdataset, found, &trdataset); + type = trdataset.type; + dns_rdataset_disassociate(&trdataset); + if (type == dns_rdatatype_nsec || + type == dns_rdatatype_nsec3 || + type == dns_rdatatype_rrsig) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + /* * Add a CNAME to the query response, including translating foo.evil.com and * *.evil.com CNAME *.example.com @@ -4762,7 +4833,8 @@ rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st, * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ - client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC; + client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | + DNS_MESSAGEFLAG_AD); return (ISC_R_SUCCESS); } @@ -5681,9 +5753,9 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) CTRACE("query_find: resume"); if (!ISC_LIST_EMPTY(client->view->rpz_zones) && - RECURSIONOK(client) && !RECURSING(client) && - (!WANTDNSSEC(client) || sigrdataset == NULL || - !dns_rdataset_isassociated(sigrdataset)) && + (RECURSIONOK(client) || !client->view->rpz_recursive_only) && + rpz_ck_dnssec(client, result, rdataset, sigrdataset) && + !RECURSING(client) && (client->query.rpz_st == NULL || (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) && !dns_name_equal(client->query.qname, dns_rootname)) { @@ -5757,10 +5829,22 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) break; case DNS_RPZ_POLICY_RECORD: result = rpz_st->m.result; - if (type == dns_rdatatype_any && - result != DNS_R_CNAME && - dns_rdataset_isassociated(rdataset)) - dns_rdataset_disassociate(rdataset); + if (qtype == dns_rdatatype_any && + result != DNS_R_CNAME) { + /* + * We will add all of the rdatasets of + * the node by iterating, setting the + * TTL then. + */ + if (dns_rdataset_isassociated(rdataset)) + dns_rdataset_disassociate(rdataset); + } else { + /* + * We will add this rdataset. + */ + rdataset->ttl = ISC_MIN(rdataset->ttl, + rpz_st->m.ttl); + } break; case DNS_RPZ_POLICY_WILDCNAME: result = dns_rdataset_first(rdataset); @@ -5799,7 +5883,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) * Turn off DNSSEC because the results of a * response policy zone cannot verify. */ - client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC; + client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC | + DNS_MESSAGEFLAG_AD); query_putrdataset(client, &sigrdataset); is_zone = ISC_TRUE; rpz_log_rewrite(client, "", rpz_st->m.policy, @@ -6747,6 +6832,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) noqname = rdataset; else noqname = NULL; + rpz_st = client->query.rpz_st; + if (rpz_st != NULL) + rdataset->ttl = ISC_MIN(rdataset->ttl, + rpz_st->m.ttl); query_addrrset(client, fname != NULL ? &fname : &tname, &rdataset, NULL, @@ -7039,8 +7128,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) */ rpz_st = client->query.rpz_st; if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0) { - rpz_clean(&rpz_st->m.zone, &rpz_st->m.db, &rpz_st->m.node, - &rpz_st->m.rdataset); + rpz_match_clear(rpz_st); rpz_st->state &= ~DNS_RPZ_DONE_QNAME; } if (rdataset != NULL) diff --git a/bin/named/server.c b/bin/named/server.c index ddfb730f4c..b4483f26a4 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1440,15 +1440,14 @@ cleanup: } static isc_result_t -configure_rpz(dns_view_t *view, const cfg_listelt_t *element) { - const cfg_obj_t *rpz_obj, *policy_obj; +configure_rpz(dns_view_t *view, const cfg_listelt_t *element, + isc_boolean_t recursive_only_def, dns_ttl_t ttl_def) +{ + const cfg_obj_t *rpz_obj, *policy_obj, *obj; const char *str; - dns_fixedname_t fixed; - dns_name_t *origin; dns_rpz_zone_t *old, *new; dns_zone_t *zone = NULL; isc_result_t result; - unsigned int l1, l2; new = isc_mem_get(view->mctx, sizeof(*new)); if (new == NULL) { @@ -1457,9 +1456,10 @@ configure_rpz(dns_view_t *view, const cfg_listelt_t *element) { } memset(new, 0, sizeof(*new)); - dns_name_init(&new->nsdname, NULL); dns_name_init(&new->origin, NULL); + dns_name_init(&new->nsdname, NULL); dns_name_init(&new->cname, NULL); + dns_name_init(&new->passthru, NULL); ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link); rpz_obj = cfg_listelt_value(element); @@ -1467,15 +1467,31 @@ configure_rpz(dns_view_t *view, const cfg_listelt_t *element) { if (cfg_obj_isvoid(policy_obj)) { new->policy = DNS_RPZ_POLICY_GIVEN; } else { - str = cfg_obj_asstring(policy_obj); + str = cfg_obj_asstring(cfg_tuple_get(policy_obj, + "policy name")); new->policy = dns_rpz_str2policy(str); INSIST(new->policy != DNS_RPZ_POLICY_ERROR); } - dns_fixedname_init(&fixed); - origin = dns_fixedname_name(&fixed); - str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "name")); - result = dns_name_fromstring(origin, str, DNS_NAME_DOWNCASE, NULL); + obj = cfg_tuple_get(rpz_obj, "recursive-only"); + if (cfg_obj_isvoid(obj)) { + new->recursive_only = recursive_only_def; + } else { + new->recursive_only = cfg_obj_asboolean(obj); + } + if (!new->recursive_only) + view->rpz_recursive_only = ISC_FALSE; + + obj = cfg_tuple_get(rpz_obj, "max-policy-ttl"); + if (cfg_obj_isuint32(obj)) { + new->max_policy_ttl = cfg_obj_asuint32(obj); + } else { + new->max_policy_ttl = ttl_def; + } + + str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name")); + result = dns_name_fromstring(&new->origin, str, DNS_NAME_DOWNCASE, + view->mctx); if (result != ISC_R_SUCCESS) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone '%s'", str); @@ -1483,31 +1499,28 @@ configure_rpz(dns_view_t *view, const cfg_listelt_t *element) { } result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE, - origin, DNS_NAME_DOWNCASE, view->mctx); + &new->origin, DNS_NAME_DOWNCASE, + view->mctx); if (result != ISC_R_SUCCESS) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid zone '%s'", str); goto cleanup; } - /* - * The origin is part of 'nsdname' so we don't need to keep it - * seperately. - */ - l1 = dns_name_countlabels(&new->nsdname); - l2 = dns_name_countlabels(origin); - dns_name_getlabelsequence(&new->nsdname, l1 - l2, l2, &new->origin); + result = dns_name_fromstring(&new->passthru, DNS_RPZ_PASSTHRU_ZONE, + DNS_NAME_DOWNCASE, view->mctx); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, + "invalid zone '%s'", str); + goto cleanup; + } - /* - * Are we configured to with the reponse policy zone? - */ result = dns_view_findzone(view, &new->origin, &zone); if (result != ISC_R_SUCCESS) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "unknown zone '%s'", str); goto cleanup; } - if (dns_zone_gettype(zone) != dns_zone_master && dns_zone_gettype(zone) != dns_zone_slave) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, @@ -1531,8 +1544,9 @@ configure_rpz(dns_view_t *view, const cfg_listelt_t *element) { } if (new->policy == DNS_RPZ_POLICY_CNAME) { - str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "cname")); - result = dns_name_fromstring(&new->cname, str, 0, view->mctx); + str = cfg_obj_asstring(cfg_tuple_get(policy_obj, "cname")); + result = dns_name_fromstring(&new->cname, str, + DNS_NAME_DOWNCASE, view->mctx); if (result != ISC_R_SUCCESS) { cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL, "invalid cname '%s'", str); @@ -2858,19 +2872,39 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig, * Make the list of response policy zone names for views that * are used for real lookups and so care about hints. */ - zonelist = NULL; - if (view->rdclass == dns_rdataclass_in && need_hints) { - obj = NULL; - result = ns_config_get(maps, "response-policy", &obj); - if (result == ISC_R_SUCCESS) - cfg_map_get(obj, "zone", &zonelist); - } + obj = NULL; + if (view->rdclass == dns_rdataclass_in && need_hints && + ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) { + const cfg_obj_t *recursive_only_obj; + const cfg_obj_t *break_dnssec_obj, *ttl_obj; + isc_boolean_t recursive_only_def; + dns_ttl_t ttl_def; - if (zonelist != NULL) { - for (element = cfg_list_first(zonelist); + recursive_only_obj = cfg_tuple_get(obj, "recursive-only"); + if (!cfg_obj_isvoid(recursive_only_obj) && + !cfg_obj_asboolean(recursive_only_obj)) + recursive_only_def = ISC_FALSE; + else + recursive_only_def = ISC_TRUE; + + break_dnssec_obj = cfg_tuple_get(obj, "break-dnssec"); + if (!cfg_obj_isvoid(break_dnssec_obj) && + cfg_obj_asboolean(break_dnssec_obj)) + view->rpz_break_dnssec = ISC_TRUE; + else + view->rpz_break_dnssec = ISC_FALSE; + + ttl_obj = cfg_tuple_get(obj, "max-policy-ttl"); + if (cfg_obj_isuint32(ttl_obj)) + ttl_def = cfg_obj_asuint32(ttl_obj); + else + ttl_def = DNS_RPZ_MAX_TTL_DEFAULT; + + for (element = cfg_list_first(cfg_tuple_get(obj, "zone list")); element != NULL; element = cfg_list_next(element)) { - result = configure_rpz(view, element); + result = configure_rpz(view, element, + recursive_only_def, ttl_def); if (result != ISC_R_SUCCESS) goto cleanup; dns_rpz_set_need(ISC_TRUE); diff --git a/bin/tests/system/rpz/clean.sh b/bin/tests/system/rpz/clean.sh index f95d11a81b..82cfcb2909 100644 --- a/bin/tests/system/rpz/clean.sh +++ b/bin/tests/system/rpz/clean.sh @@ -17,8 +17,8 @@ # Clean up after rpz tests. -rm -f proto.* dig.out* nsupdate.tmp -rm -f */named.memstats */named.run */named.rpz */session.key -rm -f ns3/bl*.db */*.jnl */*.core */*.pid -rm -f ns2/signed-tld2.db -rm -f ns2/K*.private ns2/K*.key dsset-* +rm -f proto.* dsset-* random.data trusted.conf dig.out* nsupdate.tmp ns*/*tmp +rm -f ns*/*.key ns*/*.private ns2/tld2s.db +rm -f ns3/bl*.db ns*/*switch ns5/requests ns5/example.db ns5/bl.db ns5/*.perf +rm -f */named.memstats */named.run */named.rpz */session.key +rm -f */*.jnl */*.core */*.pid diff --git a/bin/tests/system/rpz/ns1/root.db b/bin/tests/system/rpz/ns1/root.db index c691b6e419..c8d8671c7f 100644 --- a/bin/tests/system/rpz/ns1/root.db +++ b/bin/tests/system/rpz/ns1/root.db @@ -23,12 +23,9 @@ ns. A 10.53.0.1 ; rewrite responses from this zone tld2. NS ns.tld2. ns.tld2. A 10.53.0.2 -ns2.tld2. A 10.53.0.2 -; rewrite responses from this zone unless dnssec requested -signed-tld2. NS ns.signed-tld2. -ns.signed-tld2. A 10.53.0.2 -ns2.signed-tld2. A 10.53.0.2 +; rewrite responses from this secure zone unless dnssec requested (DO=1) +tld2s. NS ns.tld2. ; requests come from here tld3. NS ns.tld3. @@ -37,4 +34,3 @@ ns.tld3. A 10.53.0.3 ; rewrite responses from this zone tld4. NS ns.tld4. ns.tld4. A 10.53.0.4 -ns2.tld4. A 10.53.0.4 diff --git a/bin/tests/system/rpz/ns2/base-tld2s.db b/bin/tests/system/rpz/ns2/base-tld2s.db new file mode 100644 index 0000000000..5c3881cce5 --- /dev/null +++ b/bin/tests/system/rpz/ns2/base-tld2s.db @@ -0,0 +1,31 @@ +; Copyright (C) 2011 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: base-tld2s.db,v 1.1.2.1 2012/02/24 17:22:37 vjs Exp $ + + +; RPZ rewrite responses from this signed zone + +$TTL 120 +@ SOA tld2s. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 ) + NS ns + NS . ; check for RT 24985 +ns A 10.53.0.2 + + +a0-1 A 192.168.0.1 +a0-1-scname CNAME a0-1.tld2. + +a3-5 A 192.168.3.5 + diff --git a/bin/tests/system/rpz/ns2/named.conf b/bin/tests/system/rpz/ns2/named.conf index 32640f6362..a2fcd8254f 100644 --- a/bin/tests/system/rpz/ns2/named.conf +++ b/bin/tests/system/rpz/ns2/named.conf @@ -31,6 +31,7 @@ options { notify no; }; +include "../trusted.conf"; zone "." { type hint; file "hints"; }; zone "tld2." {type master; file "tld2.db";}; @@ -40,4 +41,5 @@ zone "sub2.tld2." {type master; file "tld2.db";}; zone "subsub.sub2.tld2." {type master; file "tld2.db";}; zone "sub3.tld2." {type master; file "tld2.db";}; zone "subsub.sub3.tld2." {type master; file "tld2.db";}; -zone "signed-tld2." {type master; file "signed-tld2.db";}; + +zone "tld2s." {type master; file "tld2s.db";}; diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db index 5f5550a94c..9ce888c6eb 100644 --- a/bin/tests/system/rpz/ns2/tld2.db +++ b/bin/tests/system/rpz/ns2/tld2.db @@ -12,7 +12,7 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: tld2.db,v 1.4 2011/10/13 01:32:33 vjs Exp $ +; $Id: tld2.db,v 1.4.4.2 2012/02/24 17:22:37 vjs Exp $ ; RPZ rewrite responses from this zone @@ -20,10 +20,8 @@ $TTL 120 @ SOA tld2. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 ) NS ns - NS ns2 NS . ; check for RT 24985 ns A 10.53.0.2 -ns2 A 10.53.0.2 txt-only TXT "txt-only-tld2" @@ -36,6 +34,8 @@ a12-cname CNAME a12 a0-1 A 192.168.0.1 AAAA 2001:2::1 TXT "a0-1 tld2 text" +a0-1-scname CNAME a0-1.tld2s. + a3-1 A 192.168.3.1 AAAA 2001:2:3::1 @@ -115,3 +115,8 @@ a5-3 A 192.168.5.3 a5-4 A 192.168.5.4 TXT "a5-4 tld2 text" + +a6-1 A 192.168.6.1 + TXT "a6-1 tld2 text" +a6-2 A 192.168.6.2 + TXT "a6-2 tld2 text" diff --git a/bin/tests/system/rpz/ns3/base.db b/bin/tests/system/rpz/ns3/base.db index 23bf71d016..cdc4466b19 100644 --- a/bin/tests/system/rpz/ns3/base.db +++ b/bin/tests/system/rpz/ns3/base.db @@ -12,14 +12,17 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: base.db,v 1.6 2011/10/13 01:32:33 vjs Exp $ +; $Id: base.db,v 1.6.4.1 2011/10/15 23:03:38 vjs Exp $ ; RPZ test +; This basic file is copied to several zone files before being used. +; Its contents are also changed with nsupdate + $TTL 120 @ SOA blx. hostmaster.ns.blx. ( 1 3600 1200 604800 60 ) - NS ns.tld. + NS ns ns A 10.53.0.3 ; Poke the radix tree a little. @@ -34,6 +37,6 @@ ns A 10.53.0.3 ; regression testing for some old crashes -redirect IN A 127.0.0.1 -*.redirect IN A 127.0.0.1 -*.credirect IN CNAME google.com. +redirect A 127.0.0.1 +*.redirect A 127.0.0.1 +*.credirect CNAME google.com. diff --git a/bin/tests/system/rpz/ns3/crash2 b/bin/tests/system/rpz/ns3/crash2 index d042c3c7bf..cd613bce90 100644 --- a/bin/tests/system/rpz/ns3/crash2 +++ b/bin/tests/system/rpz/ns3/crash2 @@ -21,8 +21,8 @@ $TTL 120 NS ns ns A 10.53.0.3 -; #18 in test1, crashed new ASSERT() in rbtdb.c -c1 A 172.16.1.1 +; #24 in test1, crashed new ASSERT() in rbtdb.c +c1 A 172.16.1.24 ; #16 in test2, crashed new ASSERT() in rbtdb.c c2 A 172.16.1.16 diff --git a/bin/tests/system/rpz/ns3/named.conf b/bin/tests/system/rpz/ns3/named.conf index 57244b8735..dfc7341d08 100644 --- a/bin/tests/system/rpz/ns3/named.conf +++ b/bin/tests/system/rpz/ns3/named.conf @@ -17,6 +17,10 @@ /* $Id: named.conf,v 1.5 2011/10/28 11:46:50 marka Exp $ */ +/* + * Main rpz test DNS server. + */ + options { query-source address 10.53.0.3; notify-source 10.53.0.3; @@ -29,13 +33,13 @@ options { notify no; response-policy { - zone "bl"; + zone "bl" max-policy-ttl 100; zone "bl-2"; - zone "bl-given" policy given; + zone "bl-given" policy given recursive-only yes; zone "bl-passthru" policy passthru; - zone "bl-no-op" policy no-op; # obsolete for passthru + zone "bl-no-op" policy no-op; # obsolete for passthru zone "bl-disabled" policy disabled; - zone "bl-nodata" policy nodata; + zone "bl-nodata" policy nodata recursive-only no; zone "bl-nxdomain" policy nxdomain; zone "bl-cname" policy cname txt-only.tld2.; zone "bl-wildcname" policy cname *.tld4.; @@ -63,6 +67,7 @@ logging { }; +// include "../trusted.conf"; zone "." { type hint; file "hints"; }; zone "bl." {type master; file "bl.db"; diff --git a/bin/tests/system/rpz/ns4/named.conf b/bin/tests/system/rpz/ns4/named.conf index 78644b127e..f8c6c902e8 100644 --- a/bin/tests/system/rpz/ns4/named.conf +++ b/bin/tests/system/rpz/ns4/named.conf @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.2 2011/10/13 04:53:06 marka Exp $ */ +/* $Id: named.conf,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $ */ controls { /* empty */ }; @@ -30,6 +30,7 @@ options { notify no; }; +include "../trusted.conf"; zone "." { type hint; file "hints"; }; zone "tld4." {type master; file "tld4.db";}; diff --git a/bin/tests/system/rpz/ns4/tld4.db b/bin/tests/system/rpz/ns4/tld4.db index 24999de993..54356d8f39 100644 --- a/bin/tests/system/rpz/ns4/tld4.db +++ b/bin/tests/system/rpz/ns4/tld4.db @@ -12,16 +12,14 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: tld4.db,v 1.2 2011/10/13 04:53:07 marka Exp $ +; $Id: tld4.db,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $ ; RPZ rewrite responses from this zone $TTL 120 @ SOA tld4. hostmaster.ns.tld4. ( 1 3600 1200 604800 60 ) NS ns - NS ns2 ns A 10.53.0.4 -ns2 A 10.53.0.4 txt-only TXT "txt-only-tld4" diff --git a/bin/tests/system/rpz/ns5/hints b/bin/tests/system/rpz/ns5/hints new file mode 100644 index 0000000000..2c0134994d --- /dev/null +++ b/bin/tests/system/rpz/ns5/hints @@ -0,0 +1,19 @@ +; Copyright (C) 2011 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: hints,v 1.1.2.1 2011/10/15 23:00:04 vjs Exp $ + + +. 0 NS ns1. +ns1. 0 A 10.53.0.1 diff --git a/bin/tests/system/rpz/ns5/named.conf b/bin/tests/system/rpz/ns5/named.conf new file mode 100644 index 0000000000..760c02cc09 --- /dev/null +++ b/bin/tests/system/rpz/ns5/named.conf @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 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: named.conf,v 1.1.2.2 2012/02/24 17:22:38 vjs Exp $ */ + + +/* + * Test rpz performance. + */ + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port 5300; + pid-file "named.pid"; + session-keyfile "session.key"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + notify no; + + # Eventually turn rpz on. + include "rpz-switch"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-md5; +}; +controls { inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; }; }; + + +include "../trusted.conf"; +zone "." {type hint; file "hints"; }; + +zone "example.com." {type master; file "example.db"; }; + +zone "bl." {type master; file "bl.db"; }; diff --git a/bin/tests/system/rpz/qperf.sh b/bin/tests/system/rpz/qperf.sh new file mode 100644 index 0000000000..3d227e2282 --- /dev/null +++ b/bin/tests/system/rpz/qperf.sh @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (C) 2011 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: qperf.sh,v 1.1.2.1 2011/10/15 23:03:37 vjs Exp $ + +for QDIR in `echo "$PATH" | tr : ' '` ../../../../contrib/queryperf; do + QPERF=$QDIR/queryperf + if test -f $QPERF -a -x $QPERF; then + echo $QPERF + exit 0 + fi +done + +exit 0 diff --git a/bin/tests/system/rpz/rpz.c b/bin/tests/system/rpz/rpz.c index f1dd3261f1..3d6a213862 100644 --- a/bin/tests/system/rpz/rpz.c +++ b/bin/tests/system/rpz/rpz.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rpz.c,v 1.3 2011/01/13 04:59:24 tbox Exp $ */ +/* $Id: rpz.c,v 1.3.226.1 2011/10/15 23:03:37 vjs Exp $ */ #include @@ -39,7 +39,7 @@ main(int argc, char **argv) #else return (1); #endif - } + } if (!strcasecmp(argv[1], "nsdname")) { #ifdef ENABLE_RPZ_NSDNAME @@ -47,8 +47,8 @@ main(int argc, char **argv) #else return (1); #endif - } + } - fputs(USAGE, stderr); - return (1); + fputs(USAGE, stderr); + return (1); } diff --git a/bin/tests/system/rpz/setup.sh b/bin/tests/system/rpz/setup.sh index d13c32b450..1bc879c789 100644 --- a/bin/tests/system/rpz/setup.sh +++ b/bin/tests/system/rpz/setup.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#! /bin/sh # # Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC") # @@ -16,16 +16,100 @@ # $Id: setup.sh,v 1.6 2012/01/07 23:46:53 tbox Exp $ +set -e + SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh -. ./clean.sh -# NO-OP is an obsolete synonym for PASSHTRU +QPERF=`sh qperf.sh` + +sh clean.sh + +# set up test policy zones. bl-2 is used to check competing zones. +# bl-{given,disabled,passthru,no-data,nxdomain,cname,wildcard,garden} +# are used to check policy overrides in named.conf. +# NO-OP is an obsolete synonym for PASSHTRU for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden; do sed -e "/SOA/s/blx/bl$NM/g" ns3/base.db >ns3/bl$NM.db done +# sign the root and a zone in ns2 ../../../tools/genrandom 400 random.data -$KEYGEN -Kns2 -q -r random.data -3 signed-tld2. > /dev/null 2>&1 -$KEYGEN -Kns2 -q -r random.data -3fk signed-tld2. > /dev/null 2>&1 -$SIGNER -S -Kns2 -o signed-tld2. -f ns2/signed-tld2.db ns2/tld2.db > /dev/null 2>&1 + +# $1=directory, $2=domain name, $3=input zone file, $4=output file +signzone () { + KEYNAME=`$KEYGEN -q -r random.data -b 512 -K $1 $2` + cat $1/$3 $1/$KEYNAME.key > $1/tmp + $SIGNER -Pp -K $1 -o $2 -f $1/$4 $1/tmp >/dev/null + sed -n -e 's/\(.*\) IN DNSKEY \([0-9]\{1,\} [0-9]\{1,\} [0-9]\{1,\}\) \(.*\)/trusted-keys {"\1" \2 "\3";};/p' $1/$KEYNAME.key >>trusted.conf + rm dsset-$2 $1/tmp +} +signzone ns2 tld2s. base-tld2s.db tld2s.db + + +# Performance checks. +# First with rpz off. +cat <ns5/rpz-switch +response-policy {zone "bl";} + recursive-only no + max-policy-ttl 90 + break-dnssec yes; +EOF + +cat <ns5/example.db +\$TTL 120 +@ SOA . hostmaster.ns.example. ( 1 3600 1200 604800 60 ) + NS ns +ns A 10.53.0.5 +EOF + +cat <ns5/bl.db +\$TTL 120 +@ SOA . hostmaster.ns.blperf. ( 1 3600 1200 604800 60 ) + NS ns +ns A 10.53.0.5 + +; used only in failure for "recursive-only no" in #8 test5 +a3-5.tld2 CNAME *. +; for "break-dnssec" in #9 test5 +a3-5.tld2s CNAME *. +; for "max-policy-ttl 90" in test5 +a3-17.tld2 500 A 17.17.17.17 + +; dummy NSDNAME policies to trigger lookups +ns-1.example.com.rpz-nsdname CNAME . +ns-2.example.com.rpz-nsdname CNAME . +ns-3.example.com.rpz-nsdname CNAME . +ns-4.example.com.rpz-nsdname CNAME . +ns-5.example.com.rpz-nsdname CNAME . +EOF + +if test -n "$QPERF"; then + # do not build the full zones if we will not use them to avoid the long + # time otherwise required to shut down the server + $PERL -e 'for ($val = 1; $val <= 65535; ++$val) { + printf("host-%d-%d\tA 192.168.%d.%d\n", + $val/256, $val%256, $val/256, $val%256); + }' >>ns5/example.db + + echo >>ns5/bl.db + echo "; rewrite some names" >>ns5/bl.db + $PERL -e 'for ($val = 2; $val <= 65535; $val += 69) { + printf("host-%d.sub%d.example.com\tCNAME\t.\n", $val/256, $val%256); + }' >>ns5/bl.db + + echo >>ns5/bl.db + echo "; rewrite with some not entirely trivial patricia trees" >>ns5/bl.db + $PERL -e 'for ($val = 3; $val <= 65535; $val += 69) { + printf("32.%d.%d.168.192.rpz-ip \tCNAME\t.\n", + $val%256, $val/256); + printf("32.%d.%d.168.192.rpz-nsip\tCNAME\t.\n", + ($val+1)%256, ($val+1)/256); + }' >>ns5/bl.db +fi + +# some psuedo-random queryperf requests +$PERL -e 'for ($cnt = $val = 1; $cnt <= 2000; ++$cnt) { + printf("host-%d.sub%d.example.com A\n", $val%256, $val/256); + $val = ($val * 9 + 32771) % 65536; + }' >ns5/requests diff --git a/bin/tests/system/rpz/test1 b/bin/tests/system/rpz/test1 index 27becfe590..aa885c3ed8 100644 --- a/bin/tests/system/rpz/test1 +++ b/bin/tests/system/rpz/test1 @@ -25,13 +25,13 @@ server 10.53.0.3 5300 ; QNAME tests ; NXDOMAIN +; 2, 20, 25 update add a0-1.tld2.bl. 300 CNAME . -update add a0-1.signed-tld2.bl. 300 CNAME . -; ; NODATA +; 3, 21 update add a3-1.tld2.bl. 300 CNAME *. ; and no assert-botch -; 5 +; 5, 22 update add a3-2.tld2.bl. 300 DNAME example.com. ; ; NXDOMAIN for a4-2-cname.tld2 via its target a4-2.tld2. @@ -57,24 +57,32 @@ update add nxc2.sub1.tld2.bl. 300 CNAME a12-cname.tld2. ; ; prefer the first conflicting zone ; 13 -update add a4-4.tld2.bl. 300 A 127.0.0.1 +update add a4-4.tld2.bl. 300 A 127.4.4.1 +update add a6-1.tld2.bl. 300 CNAME a6-1.tld2. +update add a6-2.tld2.bl. 300 A 127.6.2.1 +update add a6-1.tld2.bl. 300 A 127.6.1.1 +update add a6-2.tld2.bl. 300 CNAME a6-2.tld2. send -update add a4-4.tld2.bl-2. 300 A 127.0.0.2 +update add a4-4.tld2.bl-2. 300 A 127.4.4.2 send -; + ; wildcard CNAME -; 14 -update add a3-6.tld2.bl. 300 CNAME *.tld4. -; 15 -update add *.sub1.tld2.bl. 300 CNAME *.tld4. -; CNAME chains ; 16 -update add a4-5.tld2.bl. 300 A 127.0.0.16 +update add a3-6.tld2.bl. 300 CNAME *.tld4. ; 17 +update add *.sub1.tld2.bl. 300 CNAME *.tld4. +; CNAME chain +; 18 +update add a4-5.tld2.bl. 300 A 127.0.0.16 +; stop at first hit in CNAME chain +; 19 update add a4-6.tld2.bl. 300 CNAME . update add a4-6-cname.tld2.bl. 300 A 127.0.0.17 - -; 18 +; +; assert in rbtdb.c +; 24 update add c1.crash2.tld3.bl. 300 CNAME . - +; DO=1 without signatures, DO=0 with signatures are rewritten +; 26 - 27 +update add a0-1.tld2s.bl. 300 CNAME . send diff --git a/bin/tests/system/rpz/test2 b/bin/tests/system/rpz/test2 index 3038260f5b..2a467af930 100644 --- a/bin/tests/system/rpz/test2 +++ b/bin/tests/system/rpz/test2 @@ -19,41 +19,54 @@ ; single requests ; Separate update requests for distinct TLDs with blank lines or 'send' ; End the file with a blank line or 'send' +; CNAME targets are absolute even without trailing "." ; IP tests server 10.53.0.3 5300 ; NODATA a3-1.tld2 +; 1 update add 32.1.3.168.192.rpz-ip.bl 300 CNAME *. ; -; NXDOMAIN for 192.168.4.0/24, the network of a4-1.tld2 +; NXDOMAIN for 192.168.4.0/24, the network of a4-1.tld2 and a4-2.tld2 +; 4 update add 24.0.4.168.192.rpz-ip.bl 300 CNAME . ; -; poke hole in NXDOMAIN CIDR block to leave a4-1.tld2 unchanged +; old passthru in NXDOMAIN CIDR block to leave a4-1.tld2 unchanged +; 3 update add 32.1.4.168.192.rpz-ip.bl 300 CNAME 32.1.4.168.192 ; ; NODATA for a4-3.tld2 +; 8 update add 32.3.4.168.192.rpz-ip.bl 300 CNAME *. ; ; NXDOMAIN for IPv6 a3-1.tld2 +; 9 update add 128.1.zz.3.2.2001.rpz-ip.bl 300 CNAME . ; ; apply the policy with the lexically smallest address of 192.168.5.1 ; to an RRset of more than one A RR +; 11 update add 32.1.5.168.192.rpz-ip.bl 300 A 127.0.0.1 update add 32.2.5.168.192.rpz-ip.bl 300 A 127.0.0.2 ; ; prefer first conflicting IP zone for a5-3.tld2 +; 12 update add 32.3.5.168.192.rpz-ip.bl 300 A 127.0.0.1 send update add 32.3.5.168.192.rpz-ip.bl-2 300 A 127.0.0.2 send ; prefer QNAME to IP for a5-4.tld2 +; 13 update add 32.4.5.168.192.rpz-ip.bl 300 CNAME a12.tld2. update add a5-4.tld2.bl 300 CNAME a14.tld4. ; +; poke hole in NXDOMAIN CIDR block to leave a4-4.tld2 unchanged +; 15 +update add 32.4.4.168.192.rpz-ip.bl 300 CNAME rpz-passthru. +; ; assert in rbtdb.c ; 16 update add 32.16.1.16.172.rpz-ip.bl 300 CNAME . diff --git a/bin/tests/system/rpz/test5 b/bin/tests/system/rpz/test5 index 481118e497..a6510d30bd 100644 --- a/bin/tests/system/rpz/test5 +++ b/bin/tests/system/rpz/test5 @@ -12,7 +12,7 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test5,v 1.5 2011/10/13 01:32:33 vjs Exp $ +; $Id: test5,v 1.5.4.1 2012/02/24 17:22:37 vjs Exp $ ; Use comment lines instead of blank lines to combine update requests into @@ -24,22 +24,37 @@ server 10.53.0.3 5300 +; 1 update add a3-1.tld2.bl-given. 300 A 127.0.0.1 send +; 2 update add a3-2.tld2.bl-passthru. 300 A 127.0.0.2 send +; 3 update add a3-3.tld2.bl-no-op. 300 A 127.0.0.3 send +; 4 update add a3-4.tld2.bl-disabled. 300 A 127.0.0.4 send +; 5 - 8 update add a3-5.tld2.bl-nodata. 300 A 127.0.0.5 +; 9 - 10 +update add a3-5.tld2s.bl-nodata. 300 A 127.0.0.9 send -update add a3-6.tld2.bl-nxdomain. 300 A 127.0.0.6 +; 11 +update add a3-6.tld2.bl-nxdomain. 300 A 127.0.0.11 send -update add a3-7.tld2.bl-cname. 300 A 127.0.0.7 +; 12 +update add a3-7.tld2.bl-cname. 300 A 127.0.0.12 send -update add a3-8.tld2.bl-wildcname. 300 A 127.0.0.8 -update add *.sub9.tld2.bl-wildcname. 300 A 127.0.1.9 +; 13 +update add a3-8.tld2.bl-wildcname. 300 A 127.0.0.13 +; 14 +update add *.sub9.tld2.bl-wildcname. 300 A 127.0.1.14 send -update add a3-10.tld2.bl-garden. 300 A 127.0.0.10 +; 15 +update add a3-15.tld2.bl-garden. 300 A 127.0.0.15 +send +; 16 +update add a3-16.tld2.bl. 300 A 127.0.0.16 send diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh index 6d84146259..2df240f762 100644 --- a/bin/tests/system/rpz/tests.sh +++ b/bin/tests/system/rpz/tests.sh @@ -19,12 +19,12 @@ SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh -ns1=10.53.0.1 # root, defining the other two -ns2=10.53.0.2 # server whose answers are rewritten -ns3=10.53.0.3 # resolve that does the rewriting -ns4=10.53.0.4 # another server that is rewritten - -RNDCCMD="$RNDC -c ../common/rndc.conf -s $ns3 -p 9953" +ns=10.53.0 +ns1=$ns.1 # root, defining the others +ns2=$ns.2 # server whose answers are rewritten +ns3=$ns.3 # resolve that does the rewriting +ns4=$ns.4 # another server that is rewritten +ns5=$ns.5 # check performance with this server HAVE_CORE= @@ -44,9 +44,18 @@ fi trap 'exit 1' 1 2 15 +RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s" + digcmd () { - #echo I:dig +noadd +noauth +nosearch +time=1 +tries=1 -p 5300 $* 1>&2 - $DIG +noadd +noauth +nosearch +time=1 +tries=1 -p 5300 $* + digcmd_args="+noadd +nosearch +time=1 +tries=1 -p 5300 $*" + if ! expr "$digcmd_args" : '.*@' >/dev/null; then + digcmd_args="$digcmd_args @$ns3" + fi + if ! expr "$digcmd_args" : '.*+[no]*auth' >/dev/null; then + digcmd_args="+noauth $digcmd_args" + fi + #echo I:dig $digcmd_args 1>&2 + $DIG $digcmd_args } # set DIGNM=file name for dig output @@ -77,20 +86,41 @@ load_db () { } restart () { - $RNDCCMD stop >/dev/null 2>&1 - rm -f ns3/*.jnl - for NM in ns3/bl*.db; do - cp -f ns3/base.db $NM - done - (cd ..; $PERL start.pl --noclean --restart rpz ns3) + # try to ensure that the server really has stopped + # and won't mess with ns$1/name.pid + if test -z "$HAVE_CORE" -a -f ns$1/named.pid; then + $RNDCCMD $ns$1 halt >/dev/null 2>&1 + if test -f ns$1/named.pid; then + sleep 1 + PID=`cat ns$1/named.pid 2>/dev/null` + if test -n "$PID"; then + echo "I:killing ns$1 server $PID" + kill -9 $PID + fi + fi + fi + rm -f ns$1/*.jnl + if test -f ns$1/base.db; then + for NM in ns$1/bl*.db; do + cp -f ns$1/base.db $NM + done + fi + $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns$1 load_db } +# $1=server and irrelevant args $2=error message ckalive () { - $RNDCCMD status >/dev/null 2>&1 && return 0 + CKALIVE_NS=`expr "$1" : '.*@ns\([1-9]\).*'` + if test -z "$CKALIVE_NS"; then + CKALIVE_NS=3 + fi + eval CKALIVE_IP=\$ns$CKALIVE_NS + $RNDCCMD $CKALIVE_IP status >/dev/null 2>&1 && return 0 HAVE_CORE=yes - setret "$1" - restart + setret "$2" + # restart the server to avoid stalling waiting for it to stop + restart $CKALIVE_NS return 1 } @@ -113,15 +143,29 @@ end_group () { sed -e 's/[ ]add[ ]/ delete /' $TEST_FILE | $NSUPDATE TEST_FILE= fi + ckalive $ns3 "I:failed; ns3 server crashed and restarted" + if test "$status" -eq 0; then + # look for complaints from rpz.c + EMSGS=`grep -l 'invalid rpz' */*.run` + if test -n "$EMSGS"; then + setret "I:'invalid rpz' complaints in $EMSGS starting with:" + grep 'invalid rpz' */*.run | sed -e '4,$d' -e 's/^/I: /' + fi + # look for complaints from rpz.c and query.c + EMSGS=`grep -l 'rpz .*failed' */*.run` + if test -n "$EMSGS"; then + setret "I:'rpz failed' complaints in $EMSGS starting with:" + grep 'rpz .*failed' */*.run | sed -e '4,$d' -e 's/^/I: /' + fi + fi status=`expr $status + $ret` - ckalive "I:failed; server crashed" GROUP_NM= } # $1=dig args $2=other dig output file ckresult () { - #ckalive "I:server crashed by 'dig $1'" || return 1 - if $PERL ../digcomp.pl $DIGNM $2 >/dev/null; then + #ckalive "$1" "I:server crashed by 'dig $1'" || return 1 + if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then rm -f ${DIGNM}* return 0 fi @@ -132,8 +176,8 @@ ckresult () { # check only that the server does not crash # $1=target domain $2=optional query type nocrash () { - digcmd $* @$ns3 >/dev/null - ckalive "I:server crashed by 'dig $*'" + digcmd $* >/dev/null + ckalive "$*" "I:server crashed by 'dig $*'" } @@ -141,8 +185,10 @@ nocrash () { # $1=target domain $2=optional query type nxdomain () { make_dignm - digcmd +noauth $* @$ns3 \ - | sed -e 's/^[a-z].* IN CNAME /;xxx &/' >$DIGNM + digcmd $* \ + | sed -e 's/^[a-z].* IN CNAME /;xxx &/' \ + -e 's/^[a-z].* IN RRSIG /;xxx &/' \ + >$DIGNM ckresult "$*" proto.nxdomain } @@ -150,33 +196,37 @@ nxdomain () { # $1=target domain $2=optional query type nodata () { make_dignm - digcmd +noauth $* @$ns3 \ + digcmd $* \ | sed -e 's/^[a-z].* IN CNAME /;xxx &/' >$DIGNM ckresult "$*" proto.nodata } # check rewrite to an address # modify the output so that it is easily compared, but save the original line -# $1=IPv4 address, $2=target domain $3=optional query type +# $1=IPv4 address $2=digcmd args $3=optional TTL addr () { ADDR=$1 - shift - ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'` make_dignm - digcmd +noauth $* @$ns3 >$DIGNM - #ckalive "I:server crashed by 'dig $*'" || return - if grep -i '^[a-z].* A '"$ADDR_ESC"'$' $DIGNM >/dev/null; then - rm -f ${DIGNM}* - return 0 + digcmd $2 >$DIGNM + #ckalive "$2" "I:server crashed by 'dig $2'" || return 1 + ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'` + ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\} *\([0-9]*\) IN A\{1,4\} ${ADDR_ESC}\$/\1/p" $DIGNM` + if test -z "$ADDR_TTL"; then + setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM" + return 1 fi - setret "I:'dig $*' wrong; no A $ADDR record in $DIGNM $2" + if test -n "$3" && test "$ADDR_TTL" -ne "$3"; then + setret "I:'dig $2' wrong; TTL=$ADDR_TTL instead of $3 in $DIGNM" + return 1 + fi + rm -f ${DIGNM}* } # check that a response is not rewritten # $1=target domain $2=optional query type nochange () { make_dignm - digcmd $* @$ns3 >$DIGNM + digcmd $* >$DIGNM digcmd $* @$ns2 >${DIGNM}_OK ckresult "$*" ${DIGNM}_OK && rm -f ${DIGNM}_OK } @@ -185,23 +235,23 @@ nochange () { here () { make_dignm sed -e 's/^[ ]*//' >${DIGNM}_OK - digcmd $* @$ns3 >$DIGNM + digcmd $* >$DIGNM ckresult "$*" ${DIGNM}_OK } # make prototype files to check against rewritten results -digcmd +noauth nonexistent @$ns2 >proto.nxdomain -digcmd +noauth txt-only.tld2 @$ns2 >proto.nodata +digcmd nonexistent @$ns2 >proto.nxdomain +digcmd txt-only.tld2 @$ns2 >proto.nodata status=0 start_group "QNAME rewrites" test1 -nochange . -nxdomain a0-1.tld2 -nodata a3-1.tld2 -nodata a3-2.tld2 -nodata sub.a3-2.tld2 # 5 no crash on DNAME +nochange . # 1 do not crash or rewrite root +nxdomain a0-1.tld2 # 2 +nodata a3-1.tld2 # 3 +nodata a3-2.tld2 # 4 no crash on DNAME +nodata sub.a3-2.tld2 nxdomain a4-2.tld2 # 6 rewrite based on CNAME target nxdomain a4-2-cname.tld2 # 7 nodata a4-3-cname.tld2 # 8 @@ -209,32 +259,43 @@ addr 12.12.12.12 a4-1.sub1.tld2 # 9 A replacement addr 12.12.12.12 a4-1.sub2.tld2 # 10 A replacement with wildcard addr 12.12.12.12 nxc1.sub1.tld2 # 11 replace NXDOMAIN with CNAME addr 12.12.12.12 nxc2.sub1.tld2 # 12 replace NXDOMAIN with CNAME chain -addr 127.0.0.1 a4-4.tld2 # 13 prefer 1st conflicting QNAME zone -addr 56.56.56.56 a3-6.tld2 # 14 wildcard CNAME -addr 57.57.57.57 a3-7.sub1.tld2 # 15 wildcard CNAME -addr 127.0.0.16 a4-5-cname3.tld2 # 16 CNAME chain -addr 127.0.0.17 a4-6-cname3.tld2 # 17 stop short in CNAME chain -nxdomain c1.crash2.tld3 # 18 assert in rbtdb.c -nochange a0-1.tld2 +norecurse -nxdomain a0-1.tld2 +dnssec -nxdomain a0-1.signed-tld2 -nochange a0-1.signed-tld2 +dnssec +addr 127.4.4.1 a4-4.tld2 # 13 prefer 1st conflicting QNAME zone +nochange a6-1.tld2 # 14 +addr 127.6.2.1 a6-2.tld2 # 15 +addr 56.56.56.56 a3-6.tld2 # 16 wildcard CNAME +addr 57.57.57.57 a3-7.sub1.tld2 # 17 wildcard CNAME +addr 127.0.0.16 a4-5-cname3.tld2 # 18 CNAME chain +addr 127.0.0.17 a4-6-cname3.tld2 # 19 stop short in CNAME chain +nochange a0-1.tld2 +norecurse # 20 check that RD=1 is required +nochange a3-1.tld2 +norecurse # 21 +nochange a3-2.tld2 +norecurse # 22 +nochange sub.a3-2.tld2 +norecurse # 23 +nxdomain c1.crash2.tld3 # 24 assert in rbtdb.c +nxdomain a0-1.tld2 +dnssec # 25 simple DO=1 without signatures +nxdomain a0-1.tld2s # 26 simple DO=0 with signatures +nochange a0-1.tld2s +dnssec # 27 simple DO=1 with signatures +nxdomain a0-1s-cname.tld2s +dnssec # 28 DNSSEC too early in CNAME chain +nochange a0-1-scname.tld2 +dnssec # 29 DNSSEC on target in CNAME chain +nochange a0-1.tld2s srv +auth +dnssec # 30 no write for +DNSSEC and no record +nxdomain a0-1.tld2s srv # 31 end_group start_group "IP rewrites" test2 -nodata a3-1.tld2 -nochange a3-2.tld2 -nochange a4-1.tld2 -nxdomain a4-2.tld2 -nochange a4-2.tld2 -taaaa -nochange a4-2.tld2 -ttxt -nxdomain a4-2.tld2 -tany -nodata a4-3.tld2 -nxdomain a3-1.tld2 -tAAAA -nochange a4-1-aaaa.tld2 -tAAAA +nodata a3-1.tld2 # 1 NODATA +nochange a3-2.tld2 # 2 no policy record so no change +nochange a4-1.tld2 # 3 obsolete PASSTHRU record style +nxdomain a4-2.tld2 # 4 +nochange a4-2.tld2 -taaaa # 5 no A => no policy rewrite +nochange a4-2.tld2 -ttxt # 6 no A => no policy rewrite +nxdomain a4-2.tld2 -tany # 7 no A => no policy rewrite +nodata a4-3.tld2 # 8 +nxdomain a3-1.tld2 -taaaa # 9 IPv6 policy +nochange a4-1-aaaa.tld2 -taaaa # 10 addr 127.0.0.1 a5-1-2.tld2 # 11 prefer smallest policy address addr 127.0.0.1 a5-3.tld2 # 12 prefer first conflicting IP zone addr 14.14.14.14 a5-4.tld2 # 13 prefer QNAME to IP +nochange a5-4.tld2 +norecurse # 14 check that RD=1 is required +nochange a4-4.tld2 # 15 PASSTHRU nxdomain c2.crash2.tld3 # 16 assert in rbtdb.c end_group @@ -256,7 +317,7 @@ end_group if ./rpz nsdname; then start_group "NSDNAME rewrites" test3 nochange a3-1.tld2 - nochange a3-1.tld2 +dnssec # 2 this once caused problems + nochange a3-1.tld2 +dnssec # 2 this once caused problems nxdomain a3-1.sub1.tld2 # 3 NXDOMAIN *.sub1.tld2 by NSDNAME nxdomain a3-1.subsub.sub1.tld2 nxdomain a3-1.subsub.sub1.tld2 -tany @@ -284,21 +345,29 @@ else fi # policies in ./test5 overridden by response-policy{} in ns3/named.conf +# and in ns5/named.conf start_group "policy overrides" test5 addr 127.0.0.1 a3-1.tld2 # 1 bl-given nochange a3-2.tld2 # 2 bl-passthru nochange a3-3.tld2 # 3 bl-no-op obsolete for passthru nochange a3-4.tld2 # 4 bl-disabled nodata a3-5.tld2 # 5 bl-nodata -nxdomain a3-6.tld2 # 6 bl-nxdomain -here +noauth a3-7.tld2 -tany <<'EOF' # 7 bl_cname +nodata a3-5.tld2 +norecurse # 6 bl-nodata recursive-only no +nodata a3-5.tld2 # 7 bl-nodata +nodata a3-5.tld2 +norecurse @$ns5 # 8 bl-nodata recursive-only no +nodata a3-5.tld2s @$ns5 # 9 bl-nodata +nodata a3-5.tld2s +dnssec @$ns5 # 10 bl-nodata break-dnssec +nxdomain a3-6.tld2 # 11 bl-nxdomain +here a3-7.tld2 -tany <<'EOF' ;; status: NOERROR, x - a3-7.tld2. 300 IN CNAME txt-only.tld2. - txt-only.tld2. 120 IN TXT "txt-only-tld2" + a3-7.tld2. x IN CNAME txt-only.tld2. + txt-only.tld2. x IN TXT "txt-only-tld2" EOF -addr 58.58.58.58 a3-8.tld2 # 8 bl_wildcname -addr 59.59.59.59 a3-9.sub9.tld2 # 9 bl_wildcname -addr 12.12.12.12 a3-10.tld2 # 10 bl-garden +addr 58.58.58.58 a3-8.tld2 # 13 bl_wildcname +addr 59.59.59.59 a3-9.sub9.tld2 # 14 bl_wildcname +addr 12.12.12.12 a3-15.tld2 # 15 bl-garden via CNAME to a12.tld2 +addr 127.0.0.16 a3-16.tld2 100 # 16 bl max-policy-ttl 100 +addr 17.17.17.17 "a3-17.tld2 @$ns5" 90 # 17 ns5 bl max-policy-ttl 90 end_group # check that miscellaneous bugs are still absent @@ -312,12 +381,61 @@ for Q in RRSIG SIG ANY 'ANY +dnssec'; do done end_group -# restart the server to see if that creates a core file -if test -z "$HAVE_CORE"; then - $RNDCCMD halt - restart - test -s ns3/named.core && setret "I:found stray core file; memory leak?" + +# superficial test for major performance bugs +QPERF=`sh qperf.sh` +if test -n "$QPERF"; then + perf () { + echo "I:checking performance $1" + # don't measure the costs of -d99 + $RNDCCMD $ns5 notrace >/dev/null + $QPERF -1 -l2 -d ns5/requests -s $ns5 -p 5300 >ns5/$2.perf + ckalive $ns5 "I:failed; server #5 crashed" + } + trim () { + sed -n -e 's/.*Queries per second: *\([0-9]*\).*/\1/p' ns5/$1.perf + } + + # Dry run to prime disk cache + # Otherwise a first test of either flavor is 25% low + perf 'to prime disk cache' rpz + + # get queries/second with rpz + perf 'with rpz' rpz + + # turn off rpz and measure queries/second again + # Don't wait for a clean stop. Clean stops of this server need seconds + # until the sockets are close. 5 or 10 seconds after that, the + # server really stops and deletes named.pid. + echo "# rpz off" >ns5/rpz-switch + PID=`cat ns5/named.pid` + test -z "$PID" || kill -9 "$PID" + $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5 + perf 'without rpz' norpz + + NORPZ=`trim norpz` + RPZ=`trim rpz` + echo "I:$RPZ qps with RPZ versus $NORPZ qps without" + + # fail if RPZ costs more than 100% + NORPZ2=`expr "$NORPZ" / 2` + if test "$RPZ" -le "$NORPZ2"; then + echo "I:rpz $RPZ qps too far below non-RPZ $NORPZ qps" + status=`expr $status + 1` + fi +else + echo "I:performance not checked; queryperf not available" fi + +# restart the main test RPZ server to see if that creates a core file +if test -z "$HAVE_CORE"; then + $PERL $SYSTEMTESTTOP/stop.pl . ns3 + restart 3 + HAVE_CORE=`find ns* -name '*core*' -print` + test -z "$HAVE_CORE" || setret "I:found $HAVE_CORE; memory leak?" +fi + + echo "I:exit status: $status" exit $status diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index b8b15958a7..abd5e34d09 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -5313,7 +5313,11 @@ badresp:1,adberr:0,findfail:0,valfail:0] resolver-query-timeout number ; deny-answer-addresses { address_match_list } except-from { namelist } ; deny-answer-aliases { namelist } except-from { namelist } ; - response-policy { zone_name policy given | disabled | passthru | nxdomain | nodata | cname domain ; } ; + response-policy { zone_name + policy given | disabled | passthru | nxdomain | nodata | cname domain + recursive-only yes_or_no max-policy-ttl number ; + } recursive-only yes_or_no max-policy-ttl number + break-dnssec yes_or_no ; }; @@ -9446,17 +9450,15 @@ deny-answer-aliases { "example.net"; }; Response Policy Zone (RPZ) Rewriting - BIND 9 includes an intentionally limited - mechanism to modify DNS responses for recursive requests - somewhat similar to email anti-spam DNS blacklists. + BIND 9 includes a limited + mechanism to modify DNS responses for requests + analogous to email anti-spam DNS blacklists. Responses can be changed to deny the existence of domains(NXDOMAIN), deny the existence of IP addresses for domains (NODATA), or contain other IP addresses or data. - The actions encoded in a response policy zone (RPZ) are applied - only to queries that ask for recursion (RD=1). Response policy zones are named in the response-policy option for the view or among the global options if there is no response-policy option for the view. @@ -9467,22 +9469,21 @@ deny-answer-aliases { "example.net"; }; - There are four kinds of RPZ records, QNAME, IP, NSIP, + Four policy triggers are encoded in RPZ records, QNAME, IP, NSIP, and NSDNAME. - QNAME records are applied to query names of requests and targets + QNAME RPZ records triggered by query names of requests and targets of CNAME records resolved to generate the response. The owner name of a QNAME RPZ record is the query name relativized to the RPZ. - The second kind of RPZ record, an IP policy record, - is triggered by addresses in A and AAAA records - for the ANSWER sections of responses. - IP policy records have owner names that are - subdomains of rpz-ip relativized to the - RPZ origin name and encode an IP address or address block. - IPv4 addresses are encoded as + The second kind of RPZ trigger is an IP address in an A and AAAA + record in the ANSWER section of a response. + IP address triggers are encoded in records that have owner names + that are subdomains of rpz-ip relativized + to the RPZ origin name and encode an IP address or address block. + IPv4 trigger addresses are represented as prefixlength.B4.B3.B2.B1.rpz-ip. The prefix length must be between 1 and 32. All four bytes, B4, B3, B2, and B1, must be present. @@ -9501,43 +9502,45 @@ deny-answer-aliases { "example.net"; }; - NSDNAME policy records match names of authoritative servers - for the query name, a parent of the query name, a CNAME, - or a parent of a CNAME. + NSDNAME triggers match names of authoritative servers + for the query name, a parent of the query name, a CNAME for + query name, or a parent of a CNAME. They are encoded as subdomains of rpz-nsdomain relativized to the RPZ origin name. - NSIP policy records match IP addresses in A and AAAA RRsets - for domains that can be checked against NSDNAME policy records. - The are encoded like IP policies except as subdomains of + NSIP triggers match IP addresses in A and + AAAA RRsets for domains that can be checked against NSDNAME + policy records. + NSIP triggers are encoded like IP triggers except as subdomains of rpz-nsip. The query response is checked against all RPZs, so - two or more policy records can apply to a single response. - Because DNS responses can be rewritten according by at most a - single policy record, a single policy (other than - DISABLED policies) must be chosen. - Policies are chosen in the following order: + two or more policy records can be triggered by a response. + Because DNS responses can be rewritten according to at most one + policy record, a single record encoding an action (other than + DISABLED actions) must be chosen. + Triggers or the records that encode them are chosen in + the following order: - Among applicable zones, use the RPZ that appears first - in the response-policy option. + Choose the triggered record in the zone that appears + first in the response-policy option. - Prefer QNAME to IP to NSDNAME to NSIP policy records - in a single RPZ + Prefer QNAME to IP to NSDNAME to NSIP triggers + in a single zone. - Among applicable NSDNAME policy records, prefer the - policy record that matches the lexically smallest name + Among NSDNAME triggers, prefer the + trigger that matches the smallest name under the DNSSEC ordering. - Among IP or NSIP policy records, prefer the record + Among IP or NSIP triggers, prefer the trigger with the longest prefix. - Among records with the same prefex length, - prefer the IP or NSIP policy record that matches + Among triggers with the same prefex length, + prefer the IP or NSIP trigger that matches the smallest IP address. @@ -9545,8 +9548,8 @@ deny-answer-aliases { "example.net"; }; When the processing of a response is restarted to resolve - DNAME or CNAME records and an applicable policy record set has - not been found, + DNAME or CNAME records and a policy record set has + not been triggered, all RPZs are again consulted for the DNAME or CNAME names and addresses. @@ -9563,55 +9566,56 @@ deny-answer-aliases { "example.net"; }; - RPZ record sets are special CNAME records or one or more - of any types of DNS record except DNAME or DNSSEC. - Except when a policy record is a CNAME, there can be more - more than one record and more than one type - in a set of policy records. - Except for three kinds of CNAME records that are illegal except - in policy zones, the records in a set are used in the response as if - their owner name were the query name. They are copied to the - response as dictated by their types. + RPZ record sets are sets of any types of DNS record except + DNAME or DNSSEC that encode actions or responses to queries. - A CNAME whose target is the root domain (.) - specifies the NXDOMAIN policy, - which generates an NXDOMAIN response. + The NXDOMAIN response is encoded + by a CNAME whose target is the root domain (.) A CNAME whose target is the wildcard top-level - domain (*.) specifies the NODATA policy, + domain (*.) specifies the NODATA action, which rewrites the response to NODATA or ANCOUNT=1. - A CNAME whose target is a wildcard hostname such - as *.example.com is used normally after the astrisk (*) + The Local Data action is + represented by a set ordinary DNS records that are used + to answer queries. Queries for record types not the + set are answered with NODATA. + + A special form of local data is a CNAME whose target is a + wildcard such as *.example.com. + It is used as if were an ordinary CNAME after the astrisk (*) has been replaced with the query name. - These records are usually resolved with ordinary CNAMEs - outside the policy zones. They can be useful for logging. + The purpose for this special form is query logging in the + walled garden's authority DNS server. The PASSTHRU policy is specified - by a CNAME whose target is the variable part of its own - owner name. It causes the response to not be rewritten + by a CNAME whose target is rpz_passthru. + It causes the response to not be rewritten and is most often used to "poke holes" in policies for CIDR blocks. + (A CNAME whose target is the variable part of its owner name + is an obsolete specification of the PASSTHRU policy.) - The policies specified in individual records - in an RPZ can be overridden with a policy clause - in the response-policy option. + The actions specified in an RPZ can be overridden with a + policy clause in the + response-policy option. An organization using an RPZ provided by another organization might use this mechanism to redirect domains to its own walled garden. - GIVEN says "do not override." + GIVEN says "do not override but + perform the action specified in the zone." DISABLED causes policy records to do nothing but log what they might have done. The response to the DNS query will be written according to - any matching policy records that are not disabled. - Policy zones overridden with DISABLED should - appear first, because they will often not be logged - if a higher precedence policy is found first. + any triggered policy records that are not disabled. + Disabled policy zones should appear first, + because they will often not be logged + if a higher precedence trigger is found first. PASSTHRU causes all policy records to act as if they were CNAME records with targets the variable @@ -9630,6 +9634,37 @@ deny-answer-aliases { "example.net"; }; + + By default, the actions encoded in an RPZ are applied + only to queries that ask for recursion (RD=1). + That default can be changed for a single RPZ or all RPZs in a view + with a recursive-only no clause. + This feature is useful for serving the same zone files + both inside and outside an RFC 1918 cloud and using RPZ to + delete answers that would otherwise contain RFC 1918 values + on the externally visible name server or view. + + + + Also by default, RPZ actions are applied only to DNS requests that + either do not request DNSSEC metadata (DO=0) or when no DNSSEC + records are available for request name in the original zone (not + the response policy zone). + This default can be changed for all RPZs in a view with a + break-dnssec yes clause. + In that case, RPZ actions are applied regardless of DNSSEC. + The name of the clause option reflects the fact that results + rewritten by RPZ actions cannot verify. + + + + The TTL of a record modified by RPZ policies is set from the + TTL of the relevant record in policy zone. It is then limited + to a maximum value. + The max-policy-ttl clause changes that + maximum from its default of 5. + + For example, you might use this option statement @@ -9652,7 +9687,7 @@ bad.domain.com A 10.0.0.1 ; redirect to a walled garden AAAA 2001:2::1 ; do not rewrite (PASSTHRU) OK.DOMAIN.COM -ok.domain.com CNAME ok.domain.com. +ok.domain.com CNAME rpz-passthru. bzone.domain.com CNAME garden.example.com. @@ -9662,7 +9697,7 @@ bzone.domain.com CNAME garden.example.com. ; IP policy records that rewrite all answers for 127/8 except 127.0.0.1 8.0.0.0.127.rpz-ip CNAME . -32.1.0.0.127.rpz-ip CNAME 32.1.0.0.127. ; PASSTHRU for 127.0.0.1 +32.1.0.0.127.rpz-ip CNAME rpz-passthru. ; NSDNAME and NSIP policy records ns.domain.com.rpz-nsdname CNAME . diff --git a/lib/dns/db.c b/lib/dns/db.c index e7e4bf154f..b90fd4a3cb 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.c,v 1.99 2011/10/13 01:32:33 vjs Exp $ */ +/* $Id: db.c,v 1.99.4.1 2011/10/23 20:12:07 vjs Exp $ */ /*! \file */ @@ -1014,14 +1014,13 @@ dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) (db->methods->rpz_enabled)(db, st); } -isc_result_t +void dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *ardataset, dns_rpz_st_t *st, dns_name_t *query_qname) { - if (db->methods->rpz_findips == NULL) - return (ISC_R_NOTIMPLEMENTED); - return ((db->methods->rpz_findips)(rpz, rpz_type, zone, db, version, - ardataset, st, query_qname)); + if (db->methods->rpz_findips != NULL) + (db->methods->rpz_findips)(rpz, rpz_type, zone, db, version, + ardataset, st, query_qname); } diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 48b5a26eab..c515316d13 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.h,v 1.107 2011/10/13 01:32:34 vjs Exp $ */ +/* $Id: db.h,v 1.107.4.1 2011/10/23 20:12:08 vjs Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -173,7 +173,7 @@ typedef struct dns_dbmethods { isc_boolean_t (*isdnssec)(dns_db_t *db); dns_stats_t *(*getrrsetstats)(dns_db_t *db); void (*rpz_enabled)(dns_db_t *db, dns_rpz_st_t *st); - isc_result_t (*rpz_findips)(dns_rpz_zone_t *rpz, + void (*rpz_findips)(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, @@ -1545,7 +1545,7 @@ dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st); * DNS_RPZ_TYPE_NSDNAME records. */ -isc_result_t +void dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *ardataset, dns_rpz_st_t *st, @@ -1562,10 +1562,6 @@ dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, * \li 'ardataset' is an A or AAAA rdataset of addresses to check * \li 'found' specifies the previous best match if any or * or NULL, an empty name, 0, DNS_RPZ_POLICY_MISS, and 0 - * - * Returns: - * \li #ISC_R_SUCCESS - * \li #ISC_R_UNEXPECTED */ ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index accec2abe6..84f792902c 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -30,6 +30,7 @@ ISC_LANG_BEGINDECLS #define DNS_RPZ_IP_ZONE "rpz-ip" #define DNS_RPZ_NSIP_ZONE "rpz-nsip" #define DNS_RPZ_NSDNAME_ZONE "rpz-nsdname" +#define DNS_RPZ_PASSTHRU_ZONE "rpz-passthru" typedef isc_uint8_t dns_rpz_cidr_bits_t; @@ -66,11 +67,14 @@ typedef struct dns_rpz_zone dns_rpz_zone_t; struct dns_rpz_zone { ISC_LINK(dns_rpz_zone_t) link; - int num; + int num; /* ordinal in list of policy zones */ dns_name_t origin; /* Policy zone name */ dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */ - dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */ + dns_name_t passthru;/* DNS_RPZ_PASSTHRU_ZONE. */ dns_name_t cname; /* override value for ..._CNAME */ + dns_ttl_t max_policy_ttl; + dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */ + isc_boolean_t recursive_only; }; /* @@ -143,6 +147,7 @@ typedef struct { } dns_rpz_st_t; #define DNS_RPZ_TTL_DEFAULT 5 +#define DNS_RPZ_MAX_TTL_DEFAULT DNS_RPZ_TTL_DEFAULT /* * So various response policy zone messages can be turned up or down. @@ -152,6 +157,7 @@ typedef struct { #define DNS_RPZ_DEBUG_LEVEL1 ISC_LOG_DEBUG(1) #define DNS_RPZ_DEBUG_LEVEL2 ISC_LOG_DEBUG(2) #define DNS_RPZ_DEBUG_LEVEL3 ISC_LOG_DEBUG(3) +#define DNS_RPZ_DEBUG_QUIET (DNS_RPZ_DEBUG_LEVEL3+1) const char * dns_rpz_type2str(dns_rpz_type_t type); @@ -192,7 +198,8 @@ dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr, dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix); dns_rpz_policy_t -dns_rpz_decode_cname(dns_rdataset_t *, dns_name_t *selfname); +dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset, + dns_name_t *selfname); ISC_LANG_ENDDECLS diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 3d65243656..1ef5a64b8d 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -162,6 +162,8 @@ struct dns_view { dns_dns64list_t dns64; unsigned int dns64cnt; ISC_LIST(dns_rpz_zone_t) rpz_zones; + isc_boolean_t rpz_recursive_only; + isc_boolean_t rpz_break_dnssec; /* * Configurable data for server use only, diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 5bae6c48e1..091a261317 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -4572,7 +4572,7 @@ get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) * configured earlier than this policy zone and does not have a higher * precedence type. */ -static isc_result_t +static void rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *ardataset, dns_rpz_st_t *st, @@ -4597,7 +4597,7 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, if (rbtdb->rpz_cidr == NULL) { RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - return (ISC_R_UNEXPECTED); + return; } dns_fixedname_init(&selfnamef); @@ -4659,7 +4659,7 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, dns_name_format(qname, namebuf, sizeof(namebuf)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "rpz_findips findnode(%s): %s", + "rpz_findips findnode(%s) failed: %s", namebuf, isc_result_totext(result)); continue; } @@ -4680,7 +4680,8 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, if (zrdataset.type != dns_rdatatype_cname) { rpz_policy = DNS_RPZ_POLICY_RECORD; } else { - rpz_policy = dns_rpz_decode_cname(&zrdataset, + rpz_policy = dns_rpz_decode_cname(rpz, + &zrdataset, selfname); if (rpz_policy == DNS_RPZ_POLICY_RECORD || rpz_policy == DNS_RPZ_POLICY_WILDCNAME) @@ -4738,7 +4739,7 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, st->m.type = rpz_type; st->m.prefix = prefix; st->m.policy = rpz_policy; - st->m.ttl = ttl; + st->m.ttl = ISC_MIN(ttl, rpz->max_policy_ttl); st->m.result = result; dns_name_copy(qname, st->qname, NULL); if ((rpz_policy == DNS_RPZ_POLICY_RECORD || @@ -4755,7 +4756,6 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, } RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read); - return (ISC_R_SUCCESS); } #endif diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index e3f4989786..78658590ae 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -118,9 +118,9 @@ struct dns_rpz_cidr { isc_mem_t *mctx; isc_boolean_t have_nsdname; /* zone has NSDNAME record */ dns_rpz_cidr_node_t *root; - dns_name_t ip_name; /* RPZ_IP_ZONE.LOCALHOST. */ - dns_name_t nsip_name; /* RPZ_NSIP_ZONE.LOCALHOST. */ - dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.LOCALHOST */ + dns_name_t ip_name; /* RPZ_IP_ZONE.origin. */ + dns_name_t nsip_name; /* RPZ_NSIP_ZONE.origin. */ + dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.origin */ }; static isc_boolean_t have_rpz_zones = ISC_FALSE; @@ -183,7 +183,7 @@ dns_rpz_policy2str(dns_rpz_policy_t policy) { str = "NODATA"; break; case DNS_RPZ_POLICY_RECORD: - str = "records"; + str = "Local-Data"; break; case DNS_RPZ_POLICY_CNAME: case DNS_RPZ_POLICY_WILDCNAME: @@ -255,6 +255,8 @@ dns_rpz_view_destroy(dns_view_t *view) { ISC_LIST_UNLINK(view->rpz_zones, zone, link); if (dns_name_dynamic(&zone->origin)) dns_name_free(&zone->origin, view->mctx); + if (dns_name_dynamic(&zone->passthru)) + dns_name_free(&zone->passthru, view->mctx); if (dns_name_dynamic(&zone->nsdname)) dns_name_free(&zone->nsdname, view->mctx); if (dns_name_dynamic(&zone->cname)) @@ -427,14 +429,16 @@ new_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip, } static void -badname(int level, dns_name_t *name, const char *comment) { +badname(int level, dns_name_t *name, const char *str1, const char *str2) { char printname[DNS_NAME_FORMATSIZE]; - if (isc_log_wouldlog(dns_lctx, level)) { + if (level < DNS_RPZ_DEBUG_QUIET + && isc_log_wouldlog(dns_lctx, level)) { dns_name_format(name, printname, sizeof(printname)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, level, - "invalid rpz \"%s\"%s", printname, comment); + "invalid rpz IP address \"%s\"%s%s", + printname, str1, str2); } } @@ -566,11 +570,11 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip, dns_rpz_cidr_bits_t *tgt_prefix) { - isc_buffer_t buffer; - unsigned char data[DNS_NAME_MAXWIRE+1]; + isc_result_t result; dns_fixedname_t fname; - dns_name_t *name; - const char *cp, *end; + dns_name_t *ipname; + char ipstr[DNS_NAME_FORMATSIZE]; + const char *prefix_str, *cp, *end; char *cp2; int ip_labels; dns_rpz_cidr_bits_t bits; @@ -585,37 +589,43 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, ip_labels -= dns_name_countlabels(&cidr->ip_name); ip_labels--; if (ip_labels < 1) { - badname(level, src_name, ", too short"); + badname(level, src_name, "; too short", ""); return (ISC_R_FAILURE); } /* - * Get text for the IP address without RPZ_x_ZONE.rpz.LOCALHOST. + * Get text for the IP address */ dns_fixedname_init(&fname); - name = dns_fixedname_name(&fname); + ipname = dns_fixedname_name(&fname); dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name), - name, NULL); - isc_buffer_init(&buffer, data, sizeof(data)); - dns_name_totext(name, ISC_TRUE, &buffer); - isc_buffer_putuint8(&buffer, '\0'); - cp = isc_buffer_base(&buffer); + ipname, NULL); + dns_name_format(ipname, ipstr, sizeof(ipstr)); + end = &ipstr[strlen(ipstr)+1]; + prefix_str = ipstr; - prefix = strtoul(cp, &cp2, 10); - if (prefix < 1U || prefix > 128U || *cp2 != '.') { - badname(level, src_name, ", bad prefix length"); + prefix = strtoul(prefix_str, &cp2, 10); + if (*cp2 != '.') { + badname(level, src_name, + "; invalid leading prefix length", ""); + return (ISC_R_FAILURE); + } + *cp2 = '\0'; + if (prefix < 1U || prefix > 128U) { + badname(level, src_name, + "; invalid prefix length of ", prefix_str); return (ISC_R_FAILURE); } cp = cp2+1; - end = isc_buffer_used(&buffer); if (ip_labels == 4 && !strchr(cp, 'z')) { /* * Convert an IPv4 address * from the form "prefix.w.z.y.x" */ if (prefix > 32U) { - badname(level, src_name, "; bad IPv4 prefix length"); + badname(level, src_name, + "; invalid IPv4 prefix length of ", prefix_str); return (ISC_R_FAILURE); } prefix += 96; @@ -627,7 +637,10 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, for (i = 0; i < 32; i += 8) { l = strtoul(cp, &cp2, 10); if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) { - badname(level, src_name, "; bad IPv4 address"); + if (*cp2 == '.') + *cp2 = '\0'; + badname(level, src_name, + "; invalid IPv4 octet ", cp); return (ISC_R_FAILURE); } tgt_ip->w[3] |= l << i; @@ -654,7 +667,10 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, l = strtoul(cp, &cp2, 16); if (l > 0xffffu || (*cp2 != '.' && *cp2 != '\0')) { - badname(level, src_name, ""); + if (*cp2 == '.') + *cp2 = '\0'; + badname(level, src_name, + "; invalid IPv6 word ", cp); return (ISC_R_FAILURE); } if ((i & 1) == 0) @@ -667,7 +683,7 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, } } if (cp != end) { - badname(level, src_name, ""); + badname(level, src_name, "", ""); return (ISC_R_FAILURE); } @@ -681,7 +697,8 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, i = bits % DNS_RPZ_CIDR_WORD_BITS; aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS]; if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) { - badname(level, src_name, "; wrong prefix length"); + badname(level, src_name, + "; too small prefix length of ", prefix_str); return (ISC_R_FAILURE); } bits -= i; @@ -689,13 +706,13 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, } /* - * Convert the IPv6 address back to a canonical policy domain name + * Convert the address back to a canonical policy domain name * to ensure that it is in canonical form. */ - if (ISC_R_SUCCESS != ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t)prefix, - type, NULL, name) || - !dns_name_equal(src_name, name)) { - badname(level, src_name, "; not canonical"); + result = ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t) prefix, + type, NULL, ipname); + if (result != ISC_R_SUCCESS || !dns_name_equal(src_name, ipname)) { + badname(level, src_name, "; not canonical", ""); return (ISC_R_FAILURE); } @@ -934,6 +951,7 @@ search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, */ void dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) { + isc_result_t result; dns_rpz_cidr_key_t tgt_ip; dns_rpz_cidr_bits_t tgt_prefix; dns_rpz_type_t type; @@ -956,19 +974,22 @@ dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) { case DNS_RPZ_TYPE_BAD: return; } - if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name, - type, &tgt_ip, &tgt_prefix)) + result = name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name, + type, &tgt_ip, &tgt_prefix); + if (result != ISC_R_SUCCESS) return; - if (ISC_R_EXISTS == search(cidr, &tgt_ip, tgt_prefix, type, - ISC_TRUE, NULL) && - isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) { + result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_TRUE, NULL); + if (result == ISC_R_EXISTS && + isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) + { char printname[DNS_NAME_FORMATSIZE]; dns_name_format(name, printname, sizeof(printname)); isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "duplicate rpz name \"%s\"", printname); + "rpz add failed; \"%s\" is a duplicate name", + printname); } } @@ -978,6 +999,7 @@ dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) { */ void dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { + isc_result_t result; dns_rpz_cidr_key_t tgt_ip; dns_rpz_cidr_bits_t tgt_prefix; dns_rpz_type_t type; @@ -1010,19 +1032,14 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { /* * Do not get excited about the deletion of interior rbt nodes. */ - if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_DEBUG_LEVEL3, name, - type, &tgt_ip, &tgt_prefix)) + result = name2ipkey(cidr, DNS_RPZ_DEBUG_QUIET, name, + type, &tgt_ip, &tgt_prefix); + if (result != ISC_R_SUCCESS) return; - if (ISC_R_SUCCESS != search(cidr, &tgt_ip, tgt_prefix, type, - ISC_FALSE, &tgt)) { - if (isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) { - char printname[DNS_NAME_FORMATSIZE]; - dns_name_format(name, printname, sizeof(printname)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, - DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "missing rpz node \"%s\"", printname); - } + result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_FALSE, &tgt); + if (result != ISC_R_SUCCESS) { + badname(DNS_RPZ_ERROR_LEVEL, name, "; missing rpz node", ""); return; } @@ -1135,7 +1152,9 @@ dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr, * Translate CNAME rdata to a QNAME response policy action. */ dns_rpz_policy_t -dns_rpz_decode_cname(dns_rdataset_t *rdataset, dns_name_t *selfname) { +dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset, + dns_name_t *selfname) +{ dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_cname_t cname; isc_result_t result; @@ -1171,7 +1190,13 @@ dns_rpz_decode_cname(dns_rdataset_t *rdataset, dns_name_t *selfname) { } /* - * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. means "do not rewrite" + * CNAME PASSTHRU.origin means "do not rewrite. + */ + if (dns_name_equal(&cname.cname, &rpz->passthru)) + return (DNS_RPZ_POLICY_PASSTHRU); + + /* + * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. is obsolete PASSTHRU */ if (selfname != NULL && dns_name_equal(&cname.cname, selfname)) return (DNS_RPZ_POLICY_PASSTHRU); diff --git a/lib/dns/view.c b/lib/dns/view.c index 0f82b1ae44..699ffe12e4 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -192,6 +192,8 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->v4_aaaa = dns_v4_aaaa_ok; view->v4_aaaa_acl = NULL; ISC_LIST_INIT(view->rpz_zones); + view->rpz_recursive_only = ISC_TRUE; + view->rpz_break_dnssec = ISC_FALSE; dns_fixedname_init(&view->dlv_fixed); view->managed_keys = NULL; view->redirect = NULL; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 58b5ca4c99..bb8821cc51 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1025,66 +1025,60 @@ static cfg_type_t cfg_type_masterformat = { -/* +/*% * response-policy { * zone [ policy (given|disabled|passthru| - * nxdomain|nodata|cname ) ]; - * }; - * - * this is a chimera of doc_optional_keyvalue() and cfg_doc_enum() + * nxdomain|nodata|cname ) ] + * [ recursive-only yes|no ] + * [ max-policy-ttl number ] ; + * } [ recursive-only yes|no ] [ break-dnssec yes|no ] + * [ max-policy-ttl number ] ; */ + static void -doc_rpz_policies(cfg_printer_t *pctx, const cfg_type_t *type) { - const keyword_type_t *kw; +doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) { const char * const *p; - - kw = type->of; - cfg_print_chars(pctx, "[ ", 2); - cfg_print_cstr(pctx, kw->name); - cfg_print_chars(pctx, " ", 1); - + /* + * This is cfg_doc_enum() without the trailing " )". + */ cfg_print_chars(pctx, "( ", 2); - for (p = kw->type->of; *p != NULL; p++) { + for (p = type->of; *p != NULL; p++) { cfg_print_cstr(pctx, *p); if (p[1] != NULL) cfg_print_chars(pctx, " | ", 3); } } -/* - * print_qstring() from parser.c - */ -static void -print_rpz_cname(cfg_printer_t *pctx, const cfg_obj_t *obj) -{ - cfg_print_chars(pctx, "\"", 1); - cfg_print_ustring(pctx, obj); - cfg_print_chars(pctx, "\"", 1); -} - static void doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) { cfg_doc_terminal(pctx, type); - cfg_print_chars(pctx, " ) ]", 4); + cfg_print_chars(pctx, " )", 2); } +/* + * Parse + * given|disabled|passthru|nxdomain|nodata|cname + */ static isc_result_t -parse_rpz(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { +cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) +{ isc_result_t result; - cfg_obj_t *obj = NULL; - const cfg_tuplefielddef_t *fields = type->of; + cfg_obj_t *obj; + const cfg_tuplefielddef_t *fields; CHECK(cfg_create_tuple(pctx, type, &obj)); + + fields = type->of; CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); - CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1])); /* * parse cname domain only after "policy cname" */ - if (cfg_obj_isvoid(obj->value.tuple[1]) || - strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[1]))) { - CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2])); + if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) { + CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1])); } else { - CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2])); + CHECK(cfg_parse_obj(pctx, fields[1].type, + &obj->value.tuple[1])); } *ret = obj; @@ -1095,48 +1089,158 @@ cleanup: return (result); } +/* + * Parse a tuple consisting of any kind of required field followed + * by 2 or more optional keyvalues that can be in any order. + */ +static isc_result_t +cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) { + const cfg_tuplefielddef_t *fields, *f; + cfg_obj_t *obj; + int fn; + isc_result_t result; + + obj = NULL; + CHECK(cfg_create_tuple(pctx, type, &obj)); + + /* + * The zone first field is required and always first. + */ + fields = type->of; + CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0])); + + for (;;) { + CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING)); + if (pctx->token.type != isc_tokentype_string) + break; + + for (fn = 1, f = &fields[1]; ; ++fn, ++f) { + if (f->name == NULL) { + cfg_parser_error(pctx, 0, "unexpected '%s'", + TOKEN_STRING(pctx)); + result = ISC_R_UNEXPECTEDTOKEN; + goto cleanup; + } + if (obj->value.tuple[fn] == NULL && + strcasecmp(f->name, TOKEN_STRING(pctx)) == 0) + break; + } + + CHECK(cfg_gettoken(pctx, 0)); + CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn])); + } + + for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) { + if (obj->value.tuple[fn] == NULL) + CHECK(cfg_parse_void(pctx, NULL, + &obj->value.tuple[fn])); + } + + *ret = obj; + return (ISC_R_SUCCESS); + +cleanup: + CLEANUP_OBJ(obj); + return (result); +} + +static void +cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) { + unsigned int i; + const cfg_tuplefielddef_t *fields, *f; + const cfg_obj_t *fieldobj; + + fields = obj->type->of; + for (f = fields, i = 0; f->name != NULL; f++, i++) { + fieldobj = obj->value.tuple[i]; + if (fieldobj->type->print == cfg_print_void) + continue; + if (i != 0) { + cfg_print_chars(pctx, " ", 1); + cfg_print_cstr(pctx, f->name); + cfg_print_chars(pctx, " ", 1); + } + cfg_print_obj(pctx, fieldobj); + } +} + +static void +cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) { + const cfg_tuplefielddef_t *fields, *f; + + fields = type->of; + for (f = fields; f->name != NULL; f++) { + if (f != fields) { + cfg_print_chars(pctx, " [ ", 3); + cfg_print_cstr(pctx, f->name); + if (f->type->doc != cfg_doc_void) + cfg_print_chars(pctx, " ", 1); + } + cfg_doc_obj(pctx, f->type); + if (f != fields) + cfg_print_chars(pctx, " ]", 2); + } +} + +static keyword_type_t zone_kw = {"zone", &cfg_type_qstring}; +static cfg_type_t cfg_type_rpz_zone = { + "zone", parse_keyvalue, print_keyvalue, + doc_keyvalue, &cfg_rep_string, + &zone_kw +}; static const char *rpz_policies[] = { "given", "disabled", "passthru", "no-op", "nxdomain", "nodata", "cname", NULL }; -static cfg_type_t cfg_type_rpz_policylist = { - "policies", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, - &cfg_rep_string, &rpz_policies +static cfg_type_t cfg_type_rpz_policy_name = { + "policy name", cfg_parse_enum, cfg_print_ustring, + doc_rpz_policy, &cfg_rep_string, + &rpz_policies }; -static keyword_type_t rpz_policies_kw = { - "policy", &cfg_type_rpz_policylist -}; -static cfg_type_t cfg_type_rpz_policy = { - "optional_policy", parse_optional_keyvalue, print_keyvalue, - doc_rpz_policies, &cfg_rep_string, &rpz_policies_kw -}; -static cfg_type_t cfg_type_cname = { - "domain", cfg_parse_astring, print_rpz_cname, doc_rpz_cname, - &cfg_rep_string, NULL -}; -static cfg_tuplefielddef_t rpzone_fields[] = { - { "name", &cfg_type_astring, 0 }, - { "policy", &cfg_type_rpz_policy, 0 }, - { "cname", &cfg_type_cname, 0 }, - { NULL, NULL, 0 } -}; -static cfg_type_t cfg_type_rpzone = { - "rpzone", parse_rpz, cfg_print_tuple, cfg_doc_tuple, - &cfg_rep_tuple, rpzone_fields -}; -static cfg_clausedef_t rpz_clauses[] = { - { "zone", &cfg_type_rpzone, CFG_CLAUSEFLAG_MULTI }, - { NULL, NULL, 0 } -}; -static cfg_clausedef_t *rpz_clausesets[] = { - rpz_clauses, +static cfg_type_t cfg_type_rpz_cname = { + "quoted_string", cfg_parse_astring, NULL, + doc_rpz_cname, &cfg_rep_string, NULL }; -static cfg_type_t cfg_type_rpz = { - "rpz", cfg_parse_map, cfg_print_map, cfg_doc_map, - &cfg_rep_map, rpz_clausesets +static cfg_tuplefielddef_t rpz_policy_fields[] = { + { "policy name", &cfg_type_rpz_policy_name, 0 }, + { "cname", &cfg_type_rpz_cname, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_rpz_policy = { + "policy tuple", cfg_parse_rpz_policy, + cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, + rpz_policy_fields +}; +static cfg_tuplefielddef_t rpz_zone_fields[] = { + { "zone name", &cfg_type_rpz_zone, 0 }, + { "policy", &cfg_type_rpz_policy, 0 }, + { "recursive-only", &cfg_type_boolean, 0 }, + { "max-policy-ttl", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_rpz_tuple = { + "rpz tuple", cfg_parse_kv_tuple, + cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, + rpz_zone_fields +}; +static cfg_type_t cfg_type_rpz_list = { + "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, + &cfg_type_rpz_tuple +}; +static cfg_tuplefielddef_t rpz_fields[] = { + { "zone list", &cfg_type_rpz_list, 0 }, + { "recursive-only", &cfg_type_boolean, 0 }, + { "break-dnssec", &cfg_type_boolean, 0 }, + { "max-policy-ttl", &cfg_type_uint32, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_rpz = { + "rpz", cfg_parse_kv_tuple, + cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple, + rpz_fields }; - /*% diff --git a/util/copyrights b/util/copyrights index bad1ec51d0..a5a08f89b6 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1137,6 +1137,7 @@ ./bin/tests/system/rpz/clean.sh SH 2011,2012 ./bin/tests/system/rpz/ns1/named.conf CONF-C 2011 ./bin/tests/system/rpz/ns1/root.db ZONE 2011,2012 +./bin/tests/system/rpz/ns2/base-tld2s.db ZONE 2012 ./bin/tests/system/rpz/ns2/hints ZONE 2011 ./bin/tests/system/rpz/ns2/named.conf CONF-C 2011,2012 ./bin/tests/system/rpz/ns2/tld2.db ZONE 2011 @@ -1145,9 +1146,12 @@ ./bin/tests/system/rpz/ns3/crash2 X 2011 ./bin/tests/system/rpz/ns3/hints ZONE 2011 ./bin/tests/system/rpz/ns3/named.conf CONF-C 2011 -./bin/tests/system/rpz/ns4/hints X 2011 +./bin/tests/system/rpz/ns4/hints ZONE 2011 ./bin/tests/system/rpz/ns4/named.conf CONF-C 2011 ./bin/tests/system/rpz/ns4/tld4.db ZONE 2011 +./bin/tests/system/rpz/ns5/hints ZONE 2011 +./bin/tests/system/rpz/ns5/named.conf CONF-C 2012 +./bin/tests/system/rpz/qperf.sh SH 2012 ./bin/tests/system/rpz/rpz.c C 2011 ./bin/tests/system/rpz/setup.sh SH 2011,2012 ./bin/tests/system/rpz/test1 X 2011,2012