From: Vernon Schryver Date: Thu, 13 Oct 2011 01:32:34 +0000 (+0000) Subject: Commit rt25172 changes to HEAD including X-Git-Tag: v9.9.0a3^2~28 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=9fee08f655527a5dd849b171daeeee1dbbccca76;p=thirdparty%2Fbind9.git Commit rt25172 changes to HEAD including - fix precedence among competing rules - improve ARM text including documenting rule precedence - try to rewrite CNAME chains until first hit - new "rpz" logging channel - same fix for "NS ." as in RT 24985 --- diff --git a/bin/named/query.c b/bin/named/query.c index b74c49c683a..c78a3128d01 100644 --- a/bin/named/query.c +++ b/bin/named/query.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: query.c,v 1.373 2011/10/12 23:46:33 tbox Exp $ */ +/* $Id: query.c,v 1.374 2011/10/13 01:32:32 vjs Exp $ */ /*! \file */ @@ -830,57 +830,41 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype, } static void -rpz_log(ns_client_t *client) { - char namebuf1[DNS_NAME_FORMATSIZE]; - char namebuf2[DNS_NAME_FORMATSIZE]; - dns_rpz_st_t *st; - const char *pat; +rpz_log_rewrite(ns_client_t *client, const char *disabled, + dns_rpz_policy_t policy, dns_rpz_type_t type, + dns_name_t *rpz_qname) { + char qname_buf[DNS_NAME_FORMATSIZE]; + char rpz_qname_buf[DNS_NAME_FORMATSIZE]; - if (!ns_g_server->log_queries || - !isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL)) + if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL)) return; - st = client->query.rpz_st; - dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); - dns_name_format(st->qname, namebuf2, sizeof(namebuf2)); + dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf)); + dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf)); - switch (st->m.policy) { - case DNS_RPZ_POLICY_NO_OP: - pat ="response policy %s rewrite %s NO-OP using %s"; - break; - case DNS_RPZ_POLICY_NXDOMAIN: - pat = "response policy %s rewrite %s to NXDOMAIN using %s"; - break; - case DNS_RPZ_POLICY_NODATA: - pat = "response policy %s rewrite %s to NODATA using %s"; - break; - case DNS_RPZ_POLICY_RECORD: - case DNS_RPZ_POLICY_CNAME: - pat = "response policy %s rewrite %s using %s"; - break; - default: - INSIST(0); - } - ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY, - DNS_RPZ_INFO_LEVEL, pat, dns_rpz_type2str(st->m.type), - namebuf1, namebuf2); + ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, + DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s", + disabled, + dns_rpz_type2str(type), dns_rpz_policy2str(policy), + qname_buf, rpz_qname_buf); } static void -rpz_fail_log(ns_client_t *client, int level, dns_rpz_type_t rpz_type, - dns_name_t *name, const char *str, isc_result_t result) +rpz_log_fail(ns_client_t *client, int level, + dns_rpz_type_t rpz_type, dns_name_t *name, + const char *str, isc_result_t result) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; - if (!ns_g_server->log_queries || !isc_log_wouldlog(ns_g_lctx, level)) + if (!isc_log_wouldlog(ns_g_lctx, level)) return; dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(name, namebuf2, sizeof(namebuf2)); ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS, NS_LOGMODULE_QUERY, level, - "response policy %s rewrite %s via %s %sfailed: %s", + "rpz %s rewrite %s via %s %sfailed: %s", dns_rpz_type2str(rpz_type), namebuf1, namebuf2, str, isc_result_totext(result)); } @@ -889,9 +873,8 @@ rpz_fail_log(ns_client_t *client, int level, dns_rpz_type_t rpz_type, * Get a policy rewrite zone database. */ static isc_result_t -rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, - dns_name_t *rpz_qname, dns_zone_t **zonep, - dns_db_t **dbp, dns_dbversion_t **versionp) +rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, dns_name_t *rpz_qname, + dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp) { char namebuf1[DNS_NAME_FORMATSIZE]; char namebuf2[DNS_NAME_FORMATSIZE]; @@ -901,12 +884,11 @@ rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, result = query_getzonedb(client, rpz_qname, dns_rdatatype_any, DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version); if (result == ISC_R_SUCCESS) { - if (ns_g_server->log_queries && - isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) { + if (isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) { dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1)); dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2)); - ns_client_log(client, NS_LOGCATEGORY_QUERIES, + ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2, "try rpz %s rewrite %s via %s", dns_rpz_type2str(rpz_type), @@ -915,7 +897,7 @@ rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type, *versionp = rpz_version; return (ISC_R_SUCCESS); } - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname, "query_getzonedb() ", result); return (result); } @@ -3848,11 +3830,11 @@ rpz_st_clear(ns_client_t *client) { if (st->m.rdataset != NULL) query_putrdataset(client, &st->m.rdataset); - rpz_clean(NULL, &st->ns.db, NULL, NULL); - if (st->ns.ns_rdataset != NULL) - query_putrdataset(client, &st->ns.ns_rdataset); - if (st->ns.r_rdataset != NULL) - query_putrdataset(client, &st->ns.r_rdataset); + rpz_clean(NULL, &st->r.db, NULL, NULL); + if (st->r.ns_rdataset != NULL) + query_putrdataset(client, &st->r.ns_rdataset); + if (st->r.r_rdataset != NULL) + query_putrdataset(client, &st->r.r_rdataset); rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL); if (st->q.rdataset != NULL) @@ -3860,15 +3842,18 @@ rpz_st_clear(ns_client_t *client) { if (st->q.sigrdataset != NULL) query_putrdataset(client, &st->q.sigrdataset); st->state = 0; + st->m.type = DNS_RPZ_TYPE_BAD; + st->m.policy = DNS_RPZ_POLICY_MISS; } /* - * Get NS, A, or AAAA rrset for rpz nsdname or nsip checking. + * Get NS, A, or AAAA rrset for response policy zone checks. */ static isc_result_t -rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, - dns_db_t **dbp, dns_dbversion_t *version, - dns_rdataset_t **rdatasetp, isc_boolean_t resuming) +rpz_rrset_find(ns_client_t *client, dns_rpz_type_t rpz_type, + dns_name_t *name, dns_rdatatype_t type, + dns_db_t **dbp, dns_dbversion_t *version, + dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { dns_rpz_st_t *st; isc_boolean_t is_zone; @@ -3884,22 +3869,22 @@ rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, st = client->query.rpz_st; if ((st->state & DNS_RPZ_RECURSING) != 0) { - INSIST(st->ns.r_type == type); + INSIST(st->r.r_type == type); INSIST(dns_name_equal(name, st->r_name)); INSIST(*rdatasetp == NULL || !dns_rdataset_isassociated(*rdatasetp)); st->state &= ~DNS_RPZ_RECURSING; - *dbp = st->ns.db; - st->ns.db = NULL; + *dbp = st->r.db; + st->r.db = NULL; if (*rdatasetp != NULL) query_putrdataset(client, rdatasetp); - *rdatasetp = st->ns.r_rdataset; - st->ns.r_rdataset = NULL; - result = st->ns.r_result; + *rdatasetp = st->r.r_rdataset; + st->r.r_rdataset = NULL; + result = st->r.r_result; if (result == DNS_R_DELEGATION) { - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, - DNS_RPZ_TYPE_NSIP, name, - "rpz_ns_find() ", result); + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, + rpz_type, name, + "rpz_rrset_find(1) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; result = DNS_R_SERVFAIL; } @@ -3921,9 +3906,9 @@ rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, result = query_getdb(client, name, type, 0, &zone, dbp, &version, &is_zone); if (result != ISC_R_SUCCESS) { - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, - DNS_RPZ_TYPE_NSIP, name, "NS getdb() ", - result); + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, + rpz_type, name, + "rpz_rrset_find(2) ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; if (zone != NULL) dns_zone_detach(&zone); @@ -3936,8 +3921,9 @@ rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, node = NULL; dns_fixedname_init(&fixed); found = dns_fixedname_name(&fixed); - result = dns_db_findext(*dbp, name, version, type, 0, client->now, - &node, found, &cm, &ci, *rdatasetp, NULL); + result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK, + client->now, &node, found, + &cm, &ci, *rdatasetp, NULL); if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) { /* * Try the cache if we're authoritative for an @@ -3952,16 +3938,21 @@ rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, } rpz_clean(NULL, dbp, &node, NULL); if (result == DNS_R_DELEGATION) { + rpz_clean(NULL, NULL, NULL, rdatasetp); /* - * Recurse to get NS rrset or A or AAAA rrset for an NS name. + * Recurse for NS rrset or A or AAAA rrset for an NS. + * Do not recurse for addresses for the query name. */ - rpz_clean(NULL, NULL, NULL, rdatasetp); - dns_name_copy(name, st->r_name, NULL); - result = query_recurse(client, type, st->r_name, NULL, NULL, - resuming); - if (result == ISC_R_SUCCESS) { - st->state |= DNS_RPZ_RECURSING; - result = DNS_R_DELEGATION; + if (rpz_type == DNS_RPZ_TYPE_IP) { + result = DNS_R_NXRRSET; + } else { + dns_name_copy(name, st->r_name, NULL); + result = query_recurse(client, type, st->r_name, + NULL, NULL, resuming); + if (result == ISC_R_SUCCESS) { + st->state |= DNS_RPZ_RECURSING; + result = DNS_R_DELEGATION; + } } } return (result); @@ -3979,7 +3970,7 @@ rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, dns_dbversion_t *version; dns_zone_t *zone; dns_db_t *db; - dns_rpz_zone_t *new_rpz; + dns_rpz_zone_t *rpz; isc_result_t result; st = client->query.rpz_st; @@ -3990,16 +3981,26 @@ rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, } zone = NULL; db = NULL; - for (new_rpz = ISC_LIST_HEAD(client->view->rpz_zones); - new_rpz != NULL; - new_rpz = ISC_LIST_NEXT(new_rpz, link)) { - version = NULL; + for (rpz = ISC_LIST_HEAD(client->view->rpz_zones); + rpz != NULL; + rpz = ISC_LIST_NEXT(rpz, link)) { + /* + * Do not check policy zones that cannot replace a policy + * already known to match. + */ + if (st->m.policy != DNS_RPZ_POLICY_MISS) { + if (st->m.rpz->num < rpz->num) + break; + if (st->m.rpz->num == rpz->num && + st->m.type < rpz_type) + continue; + } /* - * Find the database for this policy zone to get its - * radix tree. + * Find the database for this policy zone to get its radix tree. */ - result = rpz_getdb(client, rpz_type, &new_rpz->origin, + version = NULL; + result = rpz_getdb(client, rpz_type, &rpz->origin, &zone, &db, &version); if (result != ISC_R_SUCCESS) { rpz_clean(&zone, &db, NULL, NULL); @@ -4011,26 +4012,31 @@ rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset, * hit, if any. Note the domain name and quality of the * best hit. */ - result = dns_db_rpz_findips(new_rpz, rpz_type, zone, db, - version, rdataset, st); - RUNTIME_CHECK(result == ISC_R_SUCCESS); + (void)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); } +/* + * Look for an A or AAAA rdataset + * and check for IP or NSIP rewrite policy rules. + */ static isc_result_t -rpz_rewrite_nsip(ns_client_t *client, dns_rdatatype_t type, dns_name_t *name, - dns_db_t **dbp, dns_dbversion_t *version, - dns_rdataset_t **rdatasetp, isc_boolean_t resuming) +rpz_rewrite_rrset(ns_client_t *client, dns_rpz_type_t rpz_type, + dns_rdatatype_t type, dns_name_t *name, + dns_db_t **dbp, dns_dbversion_t *version, + dns_rdataset_t **rdatasetp, isc_boolean_t resuming) { isc_result_t result; - result = rpz_ns_find(client, name, type, dbp, version, rdatasetp, - resuming); + result = rpz_rrset_find(client, rpz_type, name, type, dbp, version, + rdatasetp, resuming); switch (result) { case ISC_R_SUCCESS: - result = rpz_rewrite_ip(client, *rdatasetp, DNS_RPZ_TYPE_NSIP); + result = rpz_rewrite_ip(client, *rdatasetp, rpz_type); break; case DNS_R_EMPTYNAME: case DNS_R_EMPTYWILD: @@ -4038,23 +4044,76 @@ rpz_rewrite_nsip(ns_client_t *client, dns_rdatatype_t type, dns_name_t *name, case DNS_R_NCACHENXDOMAIN: case DNS_R_NXRRSET: case DNS_R_NCACHENXRRSET: + case ISC_R_NOTFOUND: result = ISC_R_SUCCESS; break; case DNS_R_DELEGATION: case DNS_R_DUPLICATE: case DNS_R_DROP: break; + case DNS_R_CNAME: + case DNS_R_DNAME: + rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, + name, "NS address rewrite rrset ", result); + result = ISC_R_SUCCESS; + break; default: if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR; - rpz_fail_log(client, ISC_LOG_WARNING, DNS_RPZ_TYPE_NSIP, - name, "NS address rewrite nsip ", result); + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, + name, "NS address rewrite rrset ", result); } break; } return (result); } +/* + * Look for both A and AAAA rdatasets + * and check for IP or NSIP rewrite policy rules. + * Look only for addresses that will be in the ANSWER section + * when checking for IP rules. + */ +static isc_result_t +rpz_rewrite_rrsets(ns_client_t *client, dns_rpz_type_t rpz_type, + dns_name_t *name, dns_rdatatype_t type, + dns_rdataset_t **rdatasetp, isc_boolean_t resuming) +{ + dns_rpz_st_t *st; + dns_dbversion_t *version; + dns_db_t *ipdb; + isc_result_t result; + + st = client->query.rpz_st; + version = NULL; + ipdb = NULL; + if ((st->state & DNS_RPZ_DONE_IPv4) == 0 && + ((rpz_type == DNS_RPZ_TYPE_NSIP) ? + (st->state & DNS_RPZ_HAVE_NSIPv4) : + (st->state & DNS_RPZ_HAVE_IP)) != 0 && + (type == dns_rdatatype_any || type == dns_rdatatype_a)) { + result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_a, + name, &ipdb, version, rdatasetp, + resuming); + if (result == ISC_R_SUCCESS) + st->state |= DNS_RPZ_DONE_IPv4; + } else { + result = ISC_R_SUCCESS; + } + if (result == ISC_R_SUCCESS && + ((rpz_type == DNS_RPZ_TYPE_NSIP) ? + (st->state & DNS_RPZ_HAVE_NSIPv6) : + (st->state & DNS_RPZ_HAVE_IP)) != 0 && + (type == dns_rdatatype_any || type == dns_rdatatype_aaaa)) { + result = rpz_rewrite_rrset(client, rpz_type, dns_rdatatype_aaaa, + name, &ipdb, version, rdatasetp, + resuming); + } + if (ipdb != NULL) + dns_db_detach(&ipdb); + return (result); +} + /* * Get the rrset from a response policy zone. */ @@ -4105,7 +4164,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, &rdsiter); if (result != ISC_R_SUCCESS) { dns_db_detachnode(*dbp, nodep); - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "allrdatasets()", result); *policyp = DNS_RPZ_POLICY_ERROR; return (DNS_R_SERVFAIL); @@ -4122,7 +4181,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, dns_rdatasetiter_destroy(&rdsiter); if (result != ISC_R_SUCCESS) { if (result != ISC_R_NOMORE) { - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "rdatasetiter", result); *policyp = DNS_RPZ_POLICY_ERROR; @@ -4152,7 +4211,8 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, policy = DNS_RPZ_POLICY_RECORD; } else { policy = dns_rpz_decode_cname(*rdatasetp, sname); - if (policy == DNS_RPZ_POLICY_RECORD && + if ((policy == DNS_RPZ_POLICY_RECORD || + policy == DNS_RPZ_POLICY_WILDCNAME) && qtype != dns_rdatatype_cname && qtype != dns_rdatatype_any) result = DNS_R_CNAME; @@ -4163,8 +4223,8 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, * DNAME policy RRs have very few if any uses that are not * better served with simple wildcards. Making the work would * require complications to get the number of labels matched - * in the name or the found name itself to the main DNS_R_DNAME - * case in query_find(). So fall through to treat them as NODATA. + * in the name or the found name to the main DNS_R_DNAME case + * in query_find(). So fall through to treat them as NODATA. */ case DNS_R_NXRRSET: policy = DNS_RPZ_POLICY_NODATA; @@ -4183,7 +4243,7 @@ rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef, default: dns_db_detach(dbp); dns_zone_detach(zonep); - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef, "", result); policy = DNS_RPZ_POLICY_ERROR; result = DNS_R_SERVFAIL; @@ -4221,7 +4281,18 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, rpz != NULL; rpz = ISC_LIST_NEXT(rpz, link)) { /* - * Construct the rule's owner name. + * Do not check policy zones that cannot replace a policy + * already known to match. + */ + if (st->m.policy != DNS_RPZ_POLICY_MISS) { + if (st->m.rpz->num < rpz->num) + break; + if (st->m.rpz->num == rpz->num && + st->m.type < rpz_type) + continue; + } + /* + * Construct the policy's owner name. */ dns_fixedname_init(&prefixf); prefix = dns_fixedname_name(&prefixf); @@ -4240,13 +4311,13 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, INSIST(result == DNS_R_NAMETOOLONG); labels = dns_name_countlabels(prefix); if (labels < 2) { - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, suffix, "concatentate() ", result); return (ISC_R_SUCCESS); } if (labels+1 == dns_name_countlabels(qname)) { - rpz_fail_log(client, DNS_RPZ_DEBUG_LEVEL1, + rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, rpz_type, suffix, "concatentate() ", result); } @@ -4254,7 +4325,7 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, } /* - * See if the qname rule (or RR) exists. + * See if the policy record exists. */ result = rpz_find(client, qtype, rpz_qname, qname, rpz_type, &zone, &db, &node, rdatasetp, &policy); @@ -4268,14 +4339,31 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, return (DNS_R_SERVFAIL); default: /* - * when more than one name or address hits a rule, - * prefer the first set of names (qname or NS), - * the first policy zone, and the smallest name + * We are dealing with names here. + * With more than one applicable policy, prefer + * the earliest configured policy, + * QNAME over IP over NSDNAME over NSIP, + * and the smallest name. + * Because of the testing above, + * we known st->m.rpz->num >= rpz->num and either + * st->m.rpz->num > rpz->num or st->m.type >= rpz_type + */ + if (st->m.policy != DNS_RPZ_POLICY_MISS && + rpz->num == st->m.rpz->num && + (st->m.type < rpz_type || + (st->m.type == rpz_type && + 0 >= dns_name_compare(rpz_qname, st->qname)))) + continue; + + /* + * Merely log DNS_RPZ_POLICY_DISABLED hits. */ - if (st->m.type == rpz_type && - rpz->num > st->m.rpz->num && - 0 <= dns_name_compare(rpz_qname, st->qname)) + if (rpz->policy == DNS_RPZ_POLICY_DISABLED) { + rpz_log_rewrite(client, "disabled ", + policy, rpz_type, rpz_qname); continue; + } + rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset); st->m.rpz = rpz; @@ -4284,7 +4372,8 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, st->m.policy = policy; st->m.result = result; dns_name_copy(rpz_qname, st->qname, NULL); - if (dns_rdataset_isassociated(*rdatasetp)) { + if (*rdatasetp != NULL && + dns_rdataset_isassociated(*rdatasetp)) { dns_rdataset_t *trdataset; trdataset = st->m.rdataset; @@ -4307,24 +4396,38 @@ rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname, return (ISC_R_SUCCESS); } +static void +rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname, + isc_result_t result, int level, const char *str) +{ + dns_rpz_st_t *st; + + st = client->query.rpz_st; + + if (str != NULL) + rpz_log_fail(client, level, DNS_RPZ_TYPE_NSIP, nsname, + str, result); + if (st->r.ns_rdataset != NULL && + dns_rdataset_isassociated(st->r.ns_rdataset)) + dns_rdataset_disassociate(st->r.ns_rdataset); + + st->r.label--; +} + /* - * Look for response policy zone NSIP and NSDNAME rewriting. + * Look for response policy zone QNAME, NSIP, and NSDNAME rewriting. */ static isc_result_t -rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, +rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, isc_result_t qresult, isc_boolean_t resuming) { dns_rpz_st_t *st; - dns_db_t *ipdb; dns_rdataset_t *rdataset; dns_fixedname_t nsnamef; dns_name_t *nsname; - dns_dbversion_t *version; + isc_boolean_t ck_ip; isc_result_t result; - ipdb = NULL; - rdataset = NULL; - st = client->query.rpz_st; if (st == NULL) { st = isc_mem_get(client->mctx, sizeof(*st)); @@ -4332,7 +4435,9 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, return (ISC_R_NOMEMORY); st->state = 0; memset(&st->m, 0, sizeof(st->m)); - memset(&st->ns, 0, sizeof(st->ns)); + st->m.type = DNS_RPZ_TYPE_BAD; + st->m.policy = DNS_RPZ_POLICY_MISS; + memset(&st->r, 0, sizeof(st->r)); memset(&st->q, 0, sizeof(st->q)); dns_fixedname_init(&st->_qnamef); dns_fixedname_init(&st->_r_namef); @@ -4342,78 +4447,147 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, st->fname = dns_fixedname_name(&st->_fnamef); client->query.rpz_st = st; } - if ((st->state & DNS_RPZ_DONE_QNAME) == 0) { - st->state = DNS_RPZ_DONE_QNAME; - st->m.type = DNS_RPZ_TYPE_BAD; - st->m.policy = DNS_RPZ_POLICY_MISS; + /* + * There is nothing to rewrite if the main query failed. + */ + switch (qresult) { + case ISC_R_SUCCESS: + case DNS_R_GLUE: + case DNS_R_ZONECUT: + ck_ip = ISC_TRUE; + break; + case DNS_R_EMPTYNAME: + case DNS_R_NXRRSET: + case DNS_R_NXDOMAIN: + case DNS_R_EMPTYWILD: + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + case DNS_R_CNAME: + case DNS_R_DNAME: + ck_ip = ISC_FALSE; + break; + case DNS_R_DELEGATION: + case ISC_R_NOTFOUND: + return (ISC_R_SUCCESS); + case ISC_R_FAILURE: + case ISC_R_TIMEDOUT: + 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()", + 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()", + qresult); + return (ISC_R_SUCCESS); + } + + rdataset = NULL; + if ((st->state & DNS_RPZ_DONE_QNAME) == 0) { /* - * Check rules for the name if this it the first time, - * i.e. we've not been recursing. + * Check rules for the query name if this it the first time + * for the current qname, i.e. we've not been recursing. + * There is a first time for each name in a CNAME chain. */ - st->state &= ~(DNS_RPZ_HAVE_IP | DNS_RPZ_HAVE_NSIPv4 | - DNS_RPZ_HAVE_NSIPv6 | DNS_RPZ_HAD_NSDNAME); result = rpz_rewrite_name(client, qtype, client->query.qname, DNS_RPZ_TYPE_QNAME, &rdataset); if (result != ISC_R_SUCCESS) goto cleanup; - if (st->m.policy != DNS_RPZ_POLICY_MISS) - goto cleanup; - if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 | - DNS_RPZ_HAD_NSDNAME)) == 0) + + st->r.label = dns_name_countlabels(client->query.qname); + + st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4); + st->state |= DNS_RPZ_DONE_QNAME; + } + + /* + * Check known IP addresses for the query name. + * Any recursion required for the query has already happened. + * Do not check addresses that will not be in the ANSWER section. + */ + if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && + (st->state & DNS_RPZ_HAVE_IP) != 0 && ck_ip) { + result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_IP, + client->query.qname, qtype, + &rdataset, resuming); + if (result != ISC_R_SUCCESS) goto cleanup; - st->ns.label = dns_name_countlabels(client->query.qname); + st->state &= ~DNS_RPZ_DONE_IPv4; + st->state |= DNS_RPZ_DONE_QNAME_IP; + } + + /* + * Stop looking for rules if there are none of the other kinds. + */ + if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 | + DNS_RPZ_HAVE_NSDNAME)) == 0) { + result = ISC_R_SUCCESS; + goto cleanup; } dns_fixedname_init(&nsnamef); dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef)); - while (st->ns.label > 1 && st->m.policy == DNS_RPZ_POLICY_MISS) { - if (st->ns.label == dns_name_countlabels(client->query.qname)) { + while (st->r.label > 1) { + /* + * Get NS rrset for each domain in the current qname. + */ + if (st->r.label == dns_name_countlabels(client->query.qname)) { nsname = client->query.qname; } else { nsname = dns_fixedname_name(&nsnamef); - dns_name_split(client->query.qname, st->ns.label, + dns_name_split(client->query.qname, st->r.label, NULL, nsname); } - if (st->ns.ns_rdataset == NULL || - !dns_rdataset_isassociated(st->ns.ns_rdataset)) { + if (st->r.ns_rdataset == NULL || + !dns_rdataset_isassociated(st->r.ns_rdataset)) { dns_db_t *db = NULL; - result = rpz_ns_find(client, nsname, dns_rdatatype_ns, - &db, NULL, &st->ns.ns_rdataset, - resuming); + result = rpz_rrset_find(client, DNS_RPZ_TYPE_NSDNAME, + nsname, dns_rdatatype_ns, + &db, NULL, &st->r.ns_rdataset, + resuming); if (db != NULL) dns_db_detach(&db); - if (result != ISC_R_SUCCESS) { - if (result == DNS_R_DELEGATION) + if (st->m.policy == DNS_RPZ_POLICY_ERROR) + goto cleanup; + switch (result) { + case ISC_R_SUCCESS: + result = dns_rdataset_first(st->r.ns_rdataset); + if (result != ISC_R_SUCCESS) goto cleanup; - if (result == DNS_R_EMPTYNAME || - result == DNS_R_NXRRSET || - result == DNS_R_EMPTYWILD || - result == DNS_R_NXDOMAIN || - result == DNS_R_NCACHENXDOMAIN || - result == DNS_R_NCACHENXRRSET || - result == DNS_R_CNAME || - result == DNS_R_DNAME) { - rpz_fail_log(client, - DNS_RPZ_DEBUG_LEVEL2, - DNS_RPZ_TYPE_NSIP, nsname, - "NS db_find() ", result); - dns_rdataset_disassociate(st->ns. - ns_rdataset); - st->ns.label--; - continue; - } - if (st->m.policy != DNS_RPZ_POLICY_ERROR) { - rpz_fail_log(client, DNS_RPZ_INFO_LEVEL, - DNS_RPZ_TYPE_NSIP, nsname, - "NS db_find() ", result); - st->m.policy = DNS_RPZ_POLICY_ERROR; - } + st->state &= ~(DNS_RPZ_DONE_NSDNAME | + DNS_RPZ_DONE_IPv4); + break; + case DNS_R_DELEGATION: goto cleanup; + case DNS_R_EMPTYNAME: + case DNS_R_NXRRSET: + case DNS_R_EMPTYWILD: + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + case ISC_R_NOTFOUND: + case DNS_R_CNAME: + case DNS_R_DNAME: + rpz_rewrite_ns_skip(client, nsname, result, + 0, NULL); + continue; + case ISC_R_TIMEDOUT: + case DNS_R_BROKENCHAIN: + case ISC_R_FAILURE: + rpz_rewrite_ns_skip(client, nsname, result, + DNS_RPZ_DEBUG_LEVEL3, + "NS db_find() "); + continue; + default: + rpz_rewrite_ns_skip(client, nsname, result, + DNS_RPZ_INFO_LEVEL, + "unrecognized NS db_find() "); + continue; } - result = dns_rdataset_first(st->ns.ns_rdataset); - if (result != ISC_R_SUCCESS) - goto cleanup; } /* * Check all NS names. @@ -4422,17 +4596,30 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, dns_rdata_ns_t ns; dns_rdata_t nsrdata = DNS_RDATA_INIT; - dns_rdataset_current(st->ns.ns_rdataset, &nsrdata); + dns_rdataset_current(st->r.ns_rdataset, &nsrdata); result = dns_rdata_tostruct(&nsrdata, &ns, NULL); dns_rdata_reset(&nsrdata); if (result != ISC_R_SUCCESS) { - rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, + rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, DNS_RPZ_TYPE_NSIP, nsname, "rdata_tostruct() ", result); st->m.policy = DNS_RPZ_POLICY_ERROR; goto cleanup; } - if ((st->state & DNS_RPZ_HAD_NSDNAME) != 0) { + /* + * Do nothing about "NS ." + */ + if (dns_name_equal(&ns.name, dns_rootname)) { + dns_rdata_freestruct(&ns); + result = dns_rdataset_next(st->r.ns_rdataset); + continue; + } + /* + * Check this NS name if we did not handle it + * during a previous recursion. + */ + if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0 && + (st->state & DNS_RPZ_HAVE_NSDNAME) != 0) { result = rpz_rewrite_name(client, qtype, &ns.name, DNS_RPZ_TYPE_NSDNAME, @@ -4441,42 +4628,23 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, dns_rdata_freestruct(&ns); goto cleanup; } + st->state |= DNS_RPZ_DONE_NSDNAME; } /* - * Check all IP addresses for this NS name, but don't - * bother without NSIP rules or with a NSDNAME hit. + * Check all IP addresses for this NS name. */ - version = NULL; - if ((st->state & DNS_RPZ_HAVE_NSIPv4) != 0 && - st->m.type != DNS_RPZ_TYPE_NSDNAME && - (st->state & DNS_RPZ_DONE_A) == 0) { - result = rpz_rewrite_nsip(client, - dns_rdatatype_a, - &ns.name, &ipdb, - version, &rdataset, - resuming); - if (result == ISC_R_SUCCESS) - st->state |= DNS_RPZ_DONE_A; - } - if (result == ISC_R_SUCCESS && - (st->state & DNS_RPZ_HAVE_NSIPv6) != 0 && - st->m.type != DNS_RPZ_TYPE_NSDNAME) { - result = rpz_rewrite_nsip(client, - dns_rdatatype_aaaa, - &ns.name, &ipdb, - version, &rdataset, - resuming); - } + result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_NSIP, + &ns.name, dns_rdatatype_any, + &rdataset, resuming); dns_rdata_freestruct(&ns); - if (ipdb != NULL) - dns_db_detach(&ipdb); if (result != ISC_R_SUCCESS) goto cleanup; - st->state &= ~DNS_RPZ_DONE_A; - result = dns_rdataset_next(st->ns.ns_rdataset); + st->state &= ~(DNS_RPZ_DONE_NSDNAME | + DNS_RPZ_DONE_IPv4); + result = dns_rdataset_next(st->r.ns_rdataset); } while (result == ISC_R_SUCCESS); - dns_rdataset_disassociate(st->ns.ns_rdataset); - st->ns.label--; + dns_rdataset_disassociate(st->r.ns_rdataset); + st->r.label--; } /* @@ -4486,31 +4654,76 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, cleanup: if (st->m.policy != DNS_RPZ_POLICY_MISS && - st->m.policy != DNS_RPZ_POLICY_NO_OP && st->m.policy != DNS_RPZ_POLICY_ERROR && st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN) st->m.policy = st->m.rpz->policy; - if (st->m.policy == DNS_RPZ_POLICY_NO_OP) - rpz_log(client); if (st->m.policy == DNS_RPZ_POLICY_MISS || - st->m.policy == DNS_RPZ_POLICY_NO_OP || - st->m.policy == DNS_RPZ_POLICY_ERROR) + st->m.policy == DNS_RPZ_POLICY_PASSTHRU || + st->m.policy == DNS_RPZ_POLICY_ERROR) { + if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU) + 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); - if (st->m.policy != DNS_RPZ_POLICY_MISS) - st->state |= DNS_RPZ_REWRITTEN; + } if (st->m.policy == DNS_RPZ_POLICY_ERROR) { st->m.type = DNS_RPZ_TYPE_BAD; result = DNS_R_SERVFAIL; } - if (rdataset != NULL) - query_putrdataset(client, &rdataset); - if ((st->state & DNS_RPZ_RECURSING) == 0) { - rpz_clean(NULL, &st->ns.db, NULL, &st->ns.ns_rdataset); - } + query_putrdataset(client, &rdataset); + if ((st->state & DNS_RPZ_RECURSING) == 0) + rpz_clean(NULL, &st->r.db, NULL, &st->r.ns_rdataset); return (result); } +/* + * Add a CNAME to the query response, including translating foo.evil.com and + * *.evil.com CNAME *.example.com + * to + * foo.evil.com CNAME foo.evil.com.example.com + */ +static isc_result_t +rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st, + dns_name_t *cname, dns_name_t *fname, isc_buffer_t *dbuf) +{ + dns_fixedname_t prefix, suffix; + unsigned int labels; + isc_result_t result; + + labels = dns_name_countlabels(cname); + if (labels > 2 && dns_name_iswildcard(cname)) { + dns_fixedname_init(&prefix); + dns_name_split(client->query.qname, 1, + dns_fixedname_name(&prefix), NULL); + dns_fixedname_init(&suffix); + dns_name_split(cname, labels-1, + NULL, dns_fixedname_name(&suffix)); + result = dns_name_concatenate(dns_fixedname_name(&prefix), + dns_fixedname_name(&suffix), + fname, NULL); + if (result == DNS_R_NAMETOOLONG) + client->message->rcode = dns_rcode_yxdomain; + } else { + result = dns_name_copy(cname, fname, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + if (result != ISC_R_SUCCESS) + return (result); + query_keepname(client, fname, dbuf); + result = query_add_cname(client, client->query.qname, + fname, dns_trust_authanswer, st->m.ttl); + if (result != ISC_R_SUCCESS) + return (result); + rpz_log_rewrite(client, "", st->m.policy, st->m.type, st->qname); + ns_client_qnamereplace(client, fname); + /* + * Turn off DNSSEC because the results of a + * response policy zone cannot verify. + */ + client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC; + return (ISC_R_SUCCESS); +} + #define MAX_RESTARTS 16 #define QUERY_ERROR(r) \ @@ -5208,14 +5421,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) rpz_st->q.sigrdataset = NULL; qtype = rpz_st->q.qtype; + rpz_st->r.db = event->db; if (event->node != NULL) - dns_db_detachnode(db, &event->node); - rpz_st->ns.db = event->db; - rpz_st->ns.r_type = event->qtype; - rpz_st->ns.r_rdataset = event->rdataset; - if (event->sigrdataset != NULL && - dns_rdataset_isassociated(event->sigrdataset)) - dns_rdataset_disassociate(event->sigrdataset); + dns_db_detachnode(event->db, &event->node); + rpz_st->r.r_type = event->qtype; + rpz_st->r.r_rdataset = event->rdataset; + query_putrdataset(client, &event->sigrdataset); } else { authoritative = ISC_FALSE; @@ -5266,7 +5477,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) } if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) != 0) { - rpz_st->ns.r_result = event->result; + rpz_st->r.r_result = event->result; result = rpz_st->q.result; isc_event_free(ISC_EVENT_PTR(&event)); } else { @@ -5429,13 +5640,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) if (!ISC_LIST_EMPTY(client->view->rpz_zones) && RECURSIONOK(client) && !RECURSING(client) && - result != DNS_R_DELEGATION && result != ISC_R_NOTFOUND && (client->query.rpz_st == NULL || (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) && !dns_name_equal(client->query.qname, dns_rootname)) { isc_result_t rresult; - rresult = rpz_rewrite(client, qtype, resuming); + rresult = rpz_rewrite(client, qtype, result, resuming); rpz_st = client->query.rpz_st; switch (rresult) { case ISC_R_SUCCESS: @@ -5466,16 +5676,19 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) RECURSE_ERROR(rresult); goto cleanup; } + if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS) + rpz_st->state |= DNS_RPZ_REWRITTEN; if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS && - rpz_st->m.policy != DNS_RPZ_POLICY_NO_OP) { - result = dns_name_copy(client->query.qname, fname, - NULL); - RUNTIME_CHECK(result == ISC_R_SUCCESS); - finish_rewrite: - rpz_clean(&zone, &db, &node, NULL); + rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU && + rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) { + if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) { + result = dns_name_copy(client->query.qname, + fname, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + rpz_clean(&zone, &db, &node, NULL); if (rpz_st->m.rdataset != NULL) { - if (rdataset != NULL) - query_putrdataset(client, &rdataset); + query_putrdataset(client, &rdataset); rdataset = rpz_st->m.rdataset; rpz_st->m.rdataset = NULL; } else if (rdataset != NULL && @@ -5489,7 +5702,6 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) zone = rpz_st->m.zone; rpz_st->m.zone = NULL; - result = rpz_st->m.result; switch (rpz_st->m.policy) { case DNS_RPZ_POLICY_NXDOMAIN: result = DNS_R_NXDOMAIN; @@ -5498,27 +5710,39 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) result = DNS_R_NXRRSET; 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); break; - case DNS_RPZ_POLICY_CNAME: - result = dns_name_copy(&rpz_st->m.rpz->cname, - fname, NULL); + case DNS_RPZ_POLICY_WILDCNAME: + result = dns_rdataset_first(rdataset); RUNTIME_CHECK(result == ISC_R_SUCCESS); - query_keepname(client, fname, dbuf); - result = query_add_cname(client, - client->query.qname, - fname, - dns_trust_authanswer, - rpz_st->m.ttl); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, + NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + dns_rdata_reset(&rdata); + result = rpz_add_cname(client, rpz_st, + &cname.cname, + fname, dbuf); + if (result != ISC_R_SUCCESS) + goto cleanup; + fname = NULL; + want_restart = ISC_TRUE; + goto cleanup; + case DNS_RPZ_POLICY_CNAME: + /* + * Add overridding CNAME from a named.conf + * response-policy statement + */ + result = rpz_add_cname(client, rpz_st, + &rpz_st->m.rpz->cname, + fname, dbuf); if (result != ISC_R_SUCCESS) goto cleanup; - ns_client_qnamereplace(client, fname); fname = NULL; - client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC; - rpz_log(client); want_restart = ISC_TRUE; goto cleanup; default: @@ -5530,11 +5754,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) * response policy zone cannot verify. */ client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC; - if (sigrdataset != NULL && - dns_rdataset_isassociated(sigrdataset)) - dns_rdataset_disassociate(sigrdataset); + query_putrdataset(client, &sigrdataset); is_zone = ISC_TRUE; - rpz_log(client); + rpz_log_rewrite(client, "", rpz_st->m.policy, + rpz_st->m.type, rpz_st->qname); } } @@ -6427,53 +6650,6 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) goto cleanup; } - /* - * Check all A and AAAA records in all response policy - * IP address zones - */ - rpz_st = client->query.rpz_st; - if (rpz_st != NULL && - (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 && - (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 && - RECURSIONOK(client) && !RECURSING(client) && - (rpz_st->state & DNS_RPZ_HAVE_IP) != 0) { - for (result = dns_rdatasetiter_first(rdsiter); - result == ISC_R_SUCCESS; - result = dns_rdatasetiter_next(rdsiter)) { - dns_rdatasetiter_current(rdsiter, rdataset); - if (rdataset->type == dns_rdatatype_a || - rdataset->type == dns_rdatatype_aaaa) - result = rpz_rewrite_ip(client, - rdataset, - DNS_RPZ_TYPE_IP); - dns_rdataset_disassociate(rdataset); - if (result != ISC_R_SUCCESS) - break; - } - if (result != ISC_R_NOMORE) { - dns_rdatasetiter_destroy(&rdsiter); - QUERY_ERROR(DNS_R_SERVFAIL); - goto cleanup; - } - switch (rpz_st->m.policy) { - case DNS_RPZ_POLICY_MISS: - break; - case DNS_RPZ_POLICY_NO_OP: - rpz_log(client); - rpz_st->state |= DNS_RPZ_REWRITTEN; - break; - case DNS_RPZ_POLICY_NXDOMAIN: - case DNS_RPZ_POLICY_NODATA: - case DNS_RPZ_POLICY_RECORD: - case DNS_RPZ_POLICY_CNAME: - dns_rdatasetiter_destroy(&rdsiter); - rpz_st->state |= DNS_RPZ_REWRITTEN; - goto finish_rewrite; - default: - INSIST(0); - } - } - /* * Calling query_addrrset() with a non-NULL dbuf is going * to either keep or release the name. We don't want it to @@ -6607,48 +6783,6 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) * we know the answer. */ - /* - * Check all A and AAAA records in all response policy - * IP address zones - */ - rpz_st = client->query.rpz_st; - if (rpz_st != NULL && - (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 && - (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 && - RECURSIONOK(client) && !RECURSING(client) && - (rpz_st->state & DNS_RPZ_HAVE_IP) != 0 && - (qtype == dns_rdatatype_aaaa || qtype == dns_rdatatype_a)) { - result = rpz_rewrite_ip(client, rdataset, - DNS_RPZ_TYPE_IP); - if (result != ISC_R_SUCCESS) { - QUERY_ERROR(DNS_R_SERVFAIL); - goto cleanup; - } - /* - * After a hit in the radix tree for the policy domain, - * either stop trying to rewrite (DNS_RPZ_POLICY_NO_OP) - * or restart to ask the ordinary database of the - * policy zone for the DNS record corresponding to the - * record in the radix tree. - */ - switch (rpz_st->m.policy) { - case DNS_RPZ_POLICY_MISS: - break; - case DNS_RPZ_POLICY_NO_OP: - rpz_log(client); - rpz_st->state |= DNS_RPZ_REWRITTEN; - break; - case DNS_RPZ_POLICY_NXDOMAIN: - case DNS_RPZ_POLICY_NODATA: - case DNS_RPZ_POLICY_RECORD: - case DNS_RPZ_POLICY_CNAME: - rpz_st->state |= DNS_RPZ_REWRITTEN; - goto finish_rewrite; - default: - INSIST(0); - } - } - #ifdef ALLOW_FILTER_AAAA_ON_V4 /* * Optionally hide AAAAs from IPv4 clients if there is an A. @@ -6856,9 +6990,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype) * General cleanup. */ rpz_st = client->query.rpz_st; - if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0) + 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_st->state &= ~DNS_RPZ_DONE_QNAME; + } if (rdataset != NULL) query_putrdataset(client, &rdataset); if (sigrdataset != NULL) diff --git a/bin/tests/system/rpz/clean.sh b/bin/tests/system/rpz/clean.sh index 2ef3d3e3127..9cf6c19b6bd 100644 --- a/bin/tests/system/rpz/clean.sh +++ b/bin/tests/system/rpz/clean.sh @@ -12,11 +12,11 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: clean.sh,v 1.3 2011/01/13 04:59:24 tbox Exp $ +# $Id: clean.sh,v 1.4 2011/10/13 01:32:32 vjs Exp $ # Clean up after rpz tests. -rm -f dig.out* nsupdate.tmp -rm -f */named.memstats */named.run */session.key -rm -f ns3/bl*.db */*.jnl +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 diff --git a/bin/tests/system/rpz/ns1/root.db b/bin/tests/system/rpz/ns1/root.db index 36fd7ab6e75..3adf0bf79ba 100644 --- a/bin/tests/system/rpz/ns1/root.db +++ b/bin/tests/system/rpz/ns1/root.db @@ -12,17 +12,24 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: root.db,v 1.3 2011/01/13 04:59:24 tbox Exp $ +; $Id: root.db,v 1.4 2011/10/13 01:32:33 vjs Exp $ $TTL 120 -@ SOA s1. hostmaster.ns.s1. ( 1 3600 1200 604800 60 ) -@ NS s1 -s1. A 10.53.0.1 +@ SOA ns. hostmaster.ns. ( 1 3600 1200 604800 60 ) +@ NS ns. +ns. A 10.53.0.1 +. 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 ; requests come from here tld3. NS ns.tld3. 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/hints b/bin/tests/system/rpz/ns2/hints index e6ad8425d66..849e2e3ab49 100644 --- a/bin/tests/system/rpz/ns2/hints +++ b/bin/tests/system/rpz/ns2/hints @@ -12,7 +12,8 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: hints,v 1.3 2011/01/13 04:59:25 tbox Exp $ +; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $ -. 0 NS s1. -s1. 0 A 10.53.0.1 + +. 0 NS ns1. +ns1. 0 A 10.53.0.1 diff --git a/bin/tests/system/rpz/ns2/named.conf b/bin/tests/system/rpz/ns2/named.conf index 5de1d51e7ce..581816fc47b 100644 --- a/bin/tests/system/rpz/ns2/named.conf +++ b/bin/tests/system/rpz/ns2/named.conf @@ -14,7 +14,8 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.3 2011/01/13 04:59:25 tbox Exp $ */ +/* $Id: named.conf,v 1.4 2011/10/13 01:32:33 vjs Exp $ */ + controls { /* empty */ }; @@ -32,6 +33,10 @@ options { zone "." { type hint; file "hints"; }; -zone "tld2." {type master; file "tld2.db";}; -zone "sub1.tld2." {type master; file "tld2.db";}; -zone "sub2.sub1.tld2." {type master; file "tld2.db";}; +zone "tld2." {type master; file "tld2.db";}; +zone "sub1.tld2." {type master; file "tld2.db";}; +zone "subsub.sub1.tld2." {type master; file "tld2.db";}; +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";}; diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db index 224f5e41907..5f5550a94c5 100644 --- a/bin/tests/system/rpz/ns2/tld2.db +++ b/bin/tests/system/rpz/ns2/tld2.db @@ -12,46 +12,106 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: tld2.db,v 1.3 2011/01/13 04:59:25 tbox Exp $ +; $Id: tld2.db,v 1.4 2011/10/13 01:32:33 vjs Exp $ + ; RPZ rewrite responses from this zone $TTL 120 @ SOA tld2. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 ) - NS @ - A 10.53.0.2 + 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" -nodata TXT "nodata" a12 A 12.12.12.12 + AAAA 2001::12 + TXT "a12 tld2 text" +a12-cname CNAME a12 a0-1 A 192.168.0.1 AAAA 2001:2::1 - TXT "a0-1 text" + TXT "a0-1 tld2 text" a3-1 A 192.168.3.1 AAAA 2001:2:3::1 - TXT "a3-1 text" + TXT "a3-1 tld2 text" a3-2 A 192.168.3.2 AAAA 2001:2:3::2 - TXT "a3-2 text" + TXT "a3-2 tld2 text" + +a3-3 A 192.168.3.3 + AAAA 2001:2:3::3 + TXT "a3-3 tld2 text" + +a3-4 A 192.168.3.4 + AAAA 2001:2:3::4 + TXT "a3-4 tld2 text" + +a3-5 A 192.168.3.5 + AAAA 2001:2:3::5 + TXT "a3-5 tld2 text" + +a3-6 A 192.168.3.6 + AAAA 2001:2:3::6 + TXT "a3-6 tld2 text" + +a3-7 A 192.168.3.7 + AAAA 2001:2:3::7 + TXT "a3-7 tld2 text" + +a3-8 A 192.168.3.8 + AAAA 2001:2:3::8 + TXT "a3-8 tld2 text" + +a3-9 A 192.168.3.9 + AAAA 2001:2:3::9 + TXT "a3-9 tld2 text" a4-1 A 192.168.4.1 AAAA 2001:2:4::1 - TXT "a4-1 text" + TXT "a4-1 tld2 text" a4-1-aaaa AAAA 2001:2:4::1 a4-2 A 192.168.4.2 AAAA 2001:2:4::2 - TXT "a4-2 text" + TXT "a4-2 tld2 text" +a4-2-cname CNAME a4-2 a4-3 A 192.168.4.3 AAAA 2001:2:4::3 - TXT "a4-3 text" + TXT "a4-3 tld2 text" +a4-3-cname CNAME a4-3 a4-4 A 192.168.4.4 AAAA 2001:2:4::4 - TXT "a4-4 text" + TXT "a4-4 tld2 text" + +a4-5 A 192.168.4.5 + AAAA 2001:2:4::5 + TXT "a4-5 tld2 text" +a4-5-cname CNAME a4-5 +a4-5-cname2 CNAME a4-5-cname +a4-5-cname3 CNAME a4-5-cname2 + +a4-6 A 192.168.4.6 + AAAA 2001:2:4::6 + TXT "a4-6 tld2 text" +a4-6-cname CNAME a4-6 +a4-6-cname2 CNAME a4-6-cname +a4-6-cname3 CNAME a4-6-cname2 + +a5-1-2 A 192.168.5.1 + A 192.168.5.2 + TXT "a5-1-2 tld2 text" -a4-5 CNAME a12 +a5-3 A 192.168.5.3 + TXT "a5-3 tld2 text" +a5-4 A 192.168.5.4 + TXT "a5-4 tld2 text" diff --git a/bin/tests/system/rpz/ns3/base.db b/bin/tests/system/rpz/ns3/base.db index 47b502ec436..23bf71d016f 100644 --- a/bin/tests/system/rpz/ns3/base.db +++ b/bin/tests/system/rpz/ns3/base.db @@ -12,13 +12,15 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: base.db,v 1.5 2011/06/09 00:42:50 marka Exp $ +; $Id: base.db,v 1.6 2011/10/13 01:32:33 vjs Exp $ + ; RPZ test $TTL 120 -@ SOA tld3. hostmaster.ns.tld3. ( 1 3600 1200 604800 60 ) -@ NS ns.utld. +@ SOA blx. hostmaster.ns.blx. ( 1 3600 1200 604800 60 ) + NS ns.tld. +ns A 10.53.0.3 ; Poke the radix tree a little. 128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip CNAME . @@ -30,7 +32,8 @@ $TTL 120 128.zz.3333.4444.0.8777.8888.rpz-ip CNAME . 127.zz.3333.4444.0.8777.8888.rpz-ip CNAME . -; for testing rrset replacement + +; regression testing for some old crashes redirect IN A 127.0.0.1 *.redirect IN A 127.0.0.1 -*.cname-redirect IN CNAME google.com. +*.credirect IN CNAME google.com. diff --git a/bin/tests/system/rpz/ns3/hints b/bin/tests/system/rpz/ns3/hints index e6ad8425d66..849e2e3ab49 100644 --- a/bin/tests/system/rpz/ns3/hints +++ b/bin/tests/system/rpz/ns3/hints @@ -12,7 +12,8 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: hints,v 1.3 2011/01/13 04:59:25 tbox Exp $ +; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $ -. 0 NS s1. -s1. 0 A 10.53.0.1 + +. 0 NS ns1. +ns1. 0 A 10.53.0.1 diff --git a/bin/tests/system/rpz/ns3/named.conf b/bin/tests/system/rpz/ns3/named.conf index b3483cbc441..c12b6f5d588 100644 --- a/bin/tests/system/rpz/ns3/named.conf +++ b/bin/tests/system/rpz/ns3/named.conf @@ -14,9 +14,8 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.3 2011/01/13 04:59:25 tbox Exp $ */ +/* $Id: named.conf,v 1.4 2011/10/13 01:32:33 vjs Exp $ */ -controls { /* empty */ }; options { query-source address 10.53.0.3; @@ -31,47 +30,62 @@ options { response-policy { zone "bl"; - zone "bl-given" policy given; - zone "bl-no-op" policy no-op; - zone "bl-nodata" policy nodata; - zone "bl-nxdomain" policy nxdomain; - zone "bl-cname" policy cname nodata.tld2.; + zone "bl-2"; + zone "bl-given" policy given; + zone "bl-passthru" policy passthru; + zone "bl-no-op" policy no-op; # obsolete for passthru + zone "bl-disabled" policy disabled; + zone "bl-nodata" policy nodata; + zone "bl-nxdomain" policy nxdomain; + zone "bl-cname" policy cname txt-only.tld2.; + zone "bl-wildcname" policy cname *.tld4.; + zone "bl-garden" policy cname a12.tld2.; }; }; key rndc_key { - secret "1234abcd8765"; - algorithm hmac-md5; + secret "1234abcd8765"; + algorithm hmac-md5; }; controls { - inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; }; + inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; }; }; logging { - category queries { default_stderr; }; + # change "-c named.conf -d 99 -g" to "-c named.conf -d 99 -f" + # in ../start.pl to check the rpz log category + channel rpz { severity debug 10; + print-category yes; print-time yes; print-severity yes; + file "named.rpz";}; + category rpz { default_stderr; rpz; }; + category queries { default_stderr; rpz; }; category query-errors { default_stderr; }; }; zone "." { type hint; file "hints"; }; +zone "bl." {type master; file "bl.db"; + allow-update {any;};}; +zone "bl-2." {type master; file "bl-2.db"; + allow-update {any;};}; +zone "bl-given." {type master; file "bl-given.db"; + allow-update {any;};}; +zone "bl-passthru." {type master; file "bl-passthru.db"; + allow-update {any;};}; +zone "bl-no-op." {type master; file "bl-no-op.db"; + allow-update {any;};}; +zone "bl-disabled." {type master; file "bl-disabled.db"; + allow-update {any;};}; +zone "bl-nodata." {type master; file "bl-nodata.db"; + allow-update {any;};}; +zone "bl-nxdomain." {type master; file "bl-nxdomain.db"; + allow-update {any;};}; +zone "bl-cname." {type master; file "bl-cname.db"; + allow-update {any;};}; +zone "bl-wildcname." {type master; file "bl-wildcname.db"; + allow-update {any;};}; +zone "bl-garden." {type master; file "bl-garden.db"; + allow-update {any;};}; -zone "bl." {type master; file "bl.db"; - allow-update {any;}; -}; -zone "bl-given." {type master; file "bl-given.db"; - allow-update {any;}; -}; -zone "bl-no-op." {type master; file "bl-no-op.db"; - allow-update {any;}; -}; -zone "bl-nodata." {type master; file "bl-nodata.db"; - allow-update {any;}; -}; -zone "bl-nxdomain." {type master; file "bl-nxdomain.db"; - allow-update {any;}; -}; -zone "bl-cname." {type master; file "bl-cname.db"; - allow-update {any;}; -}; - +zone "crash1.tld2" {type master; file "crash1";}; diff --git a/bin/tests/system/rpz/setup.sh b/bin/tests/system/rpz/setup.sh index 1d74dcfdbb1..680e2378469 100644 --- a/bin/tests/system/rpz/setup.sh +++ b/bin/tests/system/rpz/setup.sh @@ -14,10 +14,11 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: setup.sh,v 1.3 2011/01/13 04:59:24 tbox Exp $ +# $Id: setup.sh,v 1.4 2011/10/13 01:32:32 vjs Exp $ sh clean.sh -for NM in '' -given -no-op -nodata -nxdomain -cname; do - cp -f ns3/base.db ns3/bl$NM.db +# 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 diff --git a/bin/tests/system/rpz/test1 b/bin/tests/system/rpz/test1 index 3ad35d27bb6..5fd1336ce8b 100644 --- a/bin/tests/system/rpz/test1 +++ b/bin/tests/system/rpz/test1 @@ -12,19 +12,64 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test1,v 1.5 2011/06/09 03:10:17 marka Exp $ +; $Id: test1,v 1.6 2011/10/13 01:32:32 vjs Exp $ +; Use comment lines instead of blank lines to combine update requests into +; single requests +; Separate update requests for distinct TLDs with blank lines or 'send' +; End the file with a blank line or 'send' + server 10.53.0.3 5300 +; QNAME tests + ; NXDOMAIN update add a0-1.tld2.bl. 300 CNAME . - +; ; NODATA -update add a1-1.tld2.bl. 300 CNAME *. -; and no assert-botch -update add a1-2.tld2.bl. 300 DNAME example.com. - -update add *.sub1.tld2.bl. 300 A 12.12.12.12 - +update add a3-1.tld2.bl. 300 CNAME *. +; and no assert-botch +; 5 +update add a3-2.tld2.bl. 300 DNAME example.com. +; +; NXDOMAIN for a4-2-cname.tld2 via its target a4-2.tld2. +; 6 and 7 +update add a4-2.tld2.bl 300 CNAME . +; 8 +; NODATA for a4-3-cname.tld2 via its target a4-3.tld2. +update add a4-3.tld2.bl 300 CNAME *. +; +; replace the A for a4-1.sub1.tld2 with 12.12.12.12 +; 9 +update add a4-1.sub1.tld2.bl. 300 A 12.12.12.12 +; +; replace the A for *.sub2.tld2 with 12.12.12.12 +; 10 +update add a4-1.sub2.tld2.bl. 300 A 12.12.12.12 +; +; replace NXDOMAIN for {nxc1,nxc2}.sub1.tld2 with 12.12.12.12 using CNAMEs +; 11 +update add nxc1.sub1.tld2.bl. 300 CNAME a12.tld2. +; 12 +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 +send +update add a4-4.tld2.bl-2. 300 A 127.0.0.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 +; 17 +update add a4-6.tld2.bl. 300 CNAME . +update add a4-6-cname.tld2.bl. 300 A 127.0.0.17 send diff --git a/bin/tests/system/rpz/test2 b/bin/tests/system/rpz/test2 index 7fff0e48075..9f157d99d6d 100644 --- a/bin/tests/system/rpz/test2 +++ b/bin/tests/system/rpz/test2 @@ -12,24 +12,47 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test2,v 1.4 2011/01/13 19:30:41 each Exp $ +; $Id: test2,v 1.5 2011/10/13 01:32:32 vjs Exp $ +; Use comment lines instead of blank lines to combine update requests into +; single requests +; Separate update requests for distinct TLDs with blank lines or 'send' +; End the file with a blank line or 'send' + +; IP tests + server 10.53.0.3 5300 ; NODATA a3-1.tld2 update add 32.1.3.168.192.rpz-ip.bl 300 CNAME *. - -; NXDOMAIN for network of a4-1.tld2 +; +; NXDOMAIN for 192.168.4.0/24, the network of a4-1.tld2 update add 24.0.4.168.192.rpz-ip.bl 300 CNAME . - +; ; poke hole in NXDOMAIN CIDR block to leave a4-1.tld2 unchanged update add 32.1.4.168.192.rpz-ip.bl 300 CNAME 32.1.4.168.192 - -; NODATA a4-3.tld2 +; +; NODATA for a4-3.tld2 update add 32.3.4.168.192.rpz-ip.bl 300 CNAME *. - +; ; NXDOMAIN for IPv6 a3-1.tld2 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 +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 +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 +update add 32.4.5.168.192.rpz-ip.bl 300 CNAME a12.tld2. +update add a5-4.tld2.bl 300 CNAME a14.tld4. + +; wildcard CNAMEs +;update add diff --git a/bin/tests/system/rpz/test3 b/bin/tests/system/rpz/test3 index c37c73e0103..fa8575f1b1b 100644 --- a/bin/tests/system/rpz/test3 +++ b/bin/tests/system/rpz/test3 @@ -12,11 +12,33 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test3,v 1.4 2011/01/13 19:30:41 each Exp $ +; $Id: test3,v 1.5 2011/10/13 01:32:32 vjs Exp $ -server 10.53.0.3 5300 +; Use comment lines instead of blank lines to combine update requests into +; single requests +; Separate update requests for distinct TLDs with blank lines or 'send' +; End the file with a blank line or 'send' + +; NSDNAME tests -update add *.tld2.rpz-nsdname.bl. 300 CNAME . +server 10.53.0.3 5300 +; NXDOMAIN for *.sub1.tld2 by NSDNAME +update add *.sub1.tld2.rpz-nsdname.bl. 300 CNAME . +; +; walled garden for *.sub2.tld2 +update add *.sub2.tld2.rpz-nsdname.bl. 300 CNAME a12-cname.tld2. +; +; exempt a3-2.tld2 and anything in 192.168.0.0/24 +; also checks that IP policies are preferred over NSDNAME policies +update add a3-2.tld2.bl 300 CNAME a3-2.tld2. +update add 24.0.0.168.192.rpz-ip.bl 300 CNAME 24.0.0.168.192. +; +; prefer QNAME policy to NSDNAME policy +update add a4-1.tld2.bl. 300 A 12.12.12.12 +; +; prefer policy for largest NS name +update add ns.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.1 +update add ns.subsub.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.2 send diff --git a/bin/tests/system/rpz/test4 b/bin/tests/system/rpz/test4 index 9ed3d92360f..40bd83fa9dd 100644 --- a/bin/tests/system/rpz/test4 +++ b/bin/tests/system/rpz/test4 @@ -12,11 +12,26 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test4,v 1.4 2011/01/13 19:30:41 each Exp $ +; $Id: test4,v 1.5 2011/10/13 01:32:33 vjs Exp $ +; Use comment lines instead of blank lines to combine update requests into +; single requests +; Separate update requests for distinct TLDs with blank lines or 'send' +; End the file with a blank line or 'send' + +; NSIP tests + server 10.53.0.3 5300 +; NXDOMAIN for all of tld2 based on its server IP address update add 32.2.0.53.10.rpz-nsip.bl. 300 CNAME . - +; +; exempt a3-2.tld2 and anything in 192.168.0.0/24 +; also checks that IP policies are preferred over NSIP policies +update add a3-2.tld2.bl 300 CNAME a3-2.tld2. +update add 24.0.0.168.192.rpz-ip.bl 300 CNAME 24.0.0.168.192. +; +; prefer NSIP policy to NSDNAME policy +update add ns.tld2.rpz-nsdname.bl. 300 CNAME 10.0.0.1 send diff --git a/bin/tests/system/rpz/test5 b/bin/tests/system/rpz/test5 index 8fae9205190..481118e4973 100644 --- a/bin/tests/system/rpz/test5 +++ b/bin/tests/system/rpz/test5 @@ -12,25 +12,34 @@ ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR ; PERFORMANCE OF THIS SOFTWARE. -; $Id: test5,v 1.4 2011/01/13 19:30:41 each Exp $ +; $Id: test5,v 1.5 2011/10/13 01:32:33 vjs Exp $ -server 10.53.0.3 5300 -update add a3-1.tld2.bl-given. 300 CNAME . -send +; Use comment lines instead of blank lines to combine update requests into +; single requests +; Separate update requests for distinct TLDs with blank lines or 'send' +; End the file with a blank line or 'send' -server 10.53.0.3 5300 -update add a3-2.tld2.bl-no-op. 300 CNAME . -send +; the policies or replacements specified in ns3/named.conf override these server 10.53.0.3 5300 -update add a3-3.tld2.bl-nodata. 300 CNAME . -send -server 10.53.0.3 5300 -update add a3-4.tld2.bl-nxdomain. 300 CNAME *. +update add a3-1.tld2.bl-given. 300 A 127.0.0.1 send - -server 10.53.0.3 5300 -update add a3-5.tld2.bl-cname. 300 CNAME . +update add a3-2.tld2.bl-passthru. 300 A 127.0.0.2 +send +update add a3-3.tld2.bl-no-op. 300 A 127.0.0.3 +send +update add a3-4.tld2.bl-disabled. 300 A 127.0.0.4 +send +update add a3-5.tld2.bl-nodata. 300 A 127.0.0.5 +send +update add a3-6.tld2.bl-nxdomain. 300 A 127.0.0.6 +send +update add a3-7.tld2.bl-cname. 300 A 127.0.0.7 +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 +send +update add a3-10.tld2.bl-garden. 300 A 127.0.0.10 send diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh index 4c3e52c4c11..32dc2b715d4 100644 --- a/bin/tests/system/rpz/tests.sh +++ b/bin/tests/system/rpz/tests.sh @@ -12,19 +12,21 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: tests.sh,v 1.6 2011/06/09 03:10:17 marka Exp $ +# $Id: tests.sh,v 1.7 2011/10/13 01:32:33 vjs Exp $ # test response policy zones (RPZ) SYSTEMTESTTOP=.. . $SYSTEMTESTTOP/conf.sh -root=10.53.0.1 -s2=10.53.0.2 -s3=10.53.0.3 +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 -DIGCMD="$DIG +noadd +nosea +nocmd -p 5300" +RNDCCMD="$RNDC -c ../common/rndc.conf -s $ns3 -p 9953" +HAVE_CORE= USAGE="$0: [-x]" while getopts "x" c; do @@ -42,16 +44,21 @@ fi trap 'exit 1' 1 2 15 +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 $* +} + # set DIGNM=file name for dig output -# $1=target domain $2=optional query type $3=optional string -dignm () { - DIGNM=dig.out-$DIGNM_SUB-$1 - if test -n "$3"; then - DIGNM=$DIGNM-$3 - fi - if test -n "$2"; then - DIGNM=$DIGNM-`expr "x$2" : 'x-t *\(.*\)'` - fi +GROUP_NM= +TEST_NUM=0 +make_dignm () { + TEST_NUM=`expr $TEST_NUM + 1` + DIGNM=dig.out$GROUP_NM-$TEST_NUM + while test -e $DIGNM; do + TEST_NUM="$TEST_NUM+" + DIGNM=dig.out$GROUP_NM-$TEST_NUM + done } setret () { @@ -59,109 +66,159 @@ setret () { echo "$*" } +# (re)load the reponse policy zones with the rules in the file $TEST_FILE +load_db () { + if test -n "$TEST_FILE"; then + if $NSUPDATE -v $TEST_FILE; then : ; else + echo "I:failed to update policy zone with $TEST_FILE" + exit 1 + fi + fi +} + +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) + load_db +} + +ckalive () { + $RNDCCMD status >/dev/null 2>&1 && return 0 + HAVE_CORE=yes + setret "$1" + restart + return 1 +} + +# $1=message $2=optional test file name +start_group () { + ret=0 + test -n "$1" && echo "I:checking $1" + TEST_FILE=$2 + if test -n "$TEST_FILE"; then + GROUP_NM="-$TEST_FILE" + load_db + else + GROUP_NM= + fi + TEST_NUM=0 +} + +end_group () { + if test -n "$TEST_FILE"; then + sed -e 's/[ ]add[ ]/ delete /' $TEST_FILE | $NSUPDATE + TEST_FILE= + 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 + rm -f ${DIGNM}* + return 0 + fi + setret "I:'dig $1' wrong; diff $DIGNM $2" + return 1 +} + +# 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 $*'" +} + + # check rewrite to NXDOMAIN # $1=target domain $2=optional query type nxdomain () { - dignm $1 "$2" - $DIGCMD +noauth $1 $2 @$s3 >$DIGNM - $PERL ../digcomp.pl dig.out-nxdomain $DIGNM || setret " in $DIGNM" + make_dignm + digcmd +noauth $* @$ns3 \ + | sed -e 's/^[a-z].* IN CNAME /;xxx &/' >$DIGNM + ckresult "$*" proto.nxdomain } # check rewrite to NODATA # $1=target domain $2=optional query type nodata () { - dignm $1 "$2" - $DIGCMD +noauth $1 $2 @$s3 >$DIGNM - $PERL ../digcomp.pl dig.out-nodata $DIGNM || setret " in $DIGNM" + make_dignm + digcmd +noauth $* @$ns3 \ + | sed -e 's/^[a-z].* IN CNAME /;xxx &/' >$DIGNM + ckresult "$*" proto.nodata } -# check rewrite to "A 12.12.12.12" -# modify the output so that it is easily matched, but save the original line -# $1=target domain $2=optional query type -a12 () { - dignm $1 "$2" - $DIGCMD +noauth $1 $2 @$s3 \ - | sed -e "/^$1\. /{" \ - -e "s/.*/;xxx &/p" -e "s/^;xxx $1/a12.tld2/" -e '}' \ - >$DIGNM - $PERL ../digcomp.pl dig.out-a12 $DIGNM || ret=1 +# 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 +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 + fi + setret "I:'dig $*' wrong; no A $ADDR record in $DIGNM $2" } # check that a response is not rewritten # $1=target domain $2=optional query type nochange () { - dignm $1 "$2" ok - DIGNM_OK=$DIGNM - dignm $1 "$2" - $DIGCMD $1 $2 @$s3 >$DIGNM - $DIGCMD $1 $2 @$s2 >$DIGNM_OK - $PERL ../digcomp.pl $DIGNM_OK $DIGNM || ret=1 + make_dignm + digcmd $* @$ns3 >$DIGNM + digcmd $* @$ns2 >${DIGNM}_OK + ckresult "$*" ${DIGNM}_OK && rm -f ${DIGNM}_OK } -flush_db () { - if $RNDC -c ../common/rndc.conf -s $s3 -p 9953 freeze; then : ; else - echo "I:failed to freeze policy zone $1" - exit 1 - fi - if $RNDC -c ../common/rndc.conf -s $s3 -p 9953 thaw; then : ; else - echo "I:failed to thaw policy zone $1" - exit 1 - fi +# check against a 'here document' +here () { + make_dignm + sed -e 's/^[ ]*//' >${DIGNM}_OK + digcmd $* @$ns3 >$DIGNM + ckresult "$*" ${DIGNM}_OK } -# $1=message $2=test file -start_test () { - ret=0 - if test -n "$1"; then - echo "I:checking $1" - fi - PREV_FILE=$2 - if test -n "$2"; then - DIGNM_SUB=`expr "$2" : 'test\(.\)'` - if $NSUPDATE -v $PREV_FILE; then : ; else - echo "I:failed to update policy zone $1 with $2" - exit 1 - fi - #flush_db - else - DIGNM_SUB="${DIGNM_SUB}x" - fi -} - -end_test () { - if test $ret != 0; then - echo "I:failed" - else - rm -f dig.out-${DIGNM_SUB}* - fi - if test -n "$PREV_FILE"; then - sed -e 's/ add / delete /' $PREV_FILE | $NSUPDATE - status=`expr $status + $ret` - #flush_db - fi -} +# make prototype files to check against rewritten results +digcmd +noauth nonexistent @$ns2 >proto.nxdomain +digcmd +noauth txt-only.tld2 @$ns2 >proto.nodata -# make NXDOMAIN and NODATA prototypes -echo "I:making prototype RPZ NXDOMAIN, NODATA, and CNAME results" -$DIGCMD +noauth nonexistent @$s2 >dig.out-nxdomain -$DIGCMD +noauth nodata.tld2 @$s2 >dig.out-nodata -$DIGCMD +noauth a12.tld2 @$s2 >dig.out-a12 - status=0 -start_test "RPZ QNAME rewrites" test1 +start_group "QNAME rewrites" test1 +nochange . nxdomain a0-1.tld2 -nodata a1-1.tld2 -nodata a1-2.tld2 -nodata sub.a1-2.tld2 -a12 a4-1.sub1.tld2 -end_test - -start_test "RPZ IP rewrites" test2 +nodata a3-1.tld2 +nodata a3-2.tld2 +nodata sub.a3-2.tld2 # 5 no crash on DNAME +nxdomain a4-2.tld2 # 6 rewrite based on CNAME target +nxdomain a4-2-cname.tld2 # 7 +nodata a4-3-cname.tld2 # 8 +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 +end_group + +start_group "IP rewrites" test2 nodata a3-1.tld2 nochange a3-2.tld2 -nxdomain a3-99.tld2 nochange a4-1.tld2 nxdomain a4-2.tld2 nochange a4-2.tld2 -taaaa @@ -170,9 +227,13 @@ nxdomain a4-2.tld2 -tany nodata a4-3.tld2 nxdomain a3-1.tld2 -tAAAA nochange a4-1-aaaa.tld2 -tAAAA -end_test +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 +end_group -start_test "RPZ radix tree deletions" +# check that IP addresses for previous group were deleted from the radix tree +start_group "radix tree deletions" nochange a3-1.tld2 nochange a3-2.tld2 nochange a4-1.tld2 @@ -183,94 +244,73 @@ nochange a4-2.tld2 -tany nochange a4-3.tld2 nochange a3-1.tld2 -tAAAA nochange a4-1-aaaa.tld2 -tAAAA -end_test +nochange a5-1-2.tld2 +end_group if ./rpz nsdname; then - start_test "RPZ NSDNAME rewrites" test3 + start_group "NSDNAME rewrites" test3 nochange a3-1.tld2 - nxdomain a3-1.sub1.tld2 - nxdomain a3-1.sub2.sub1.tld2 - end_test + 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 + addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2 + nochange a3-2.tld2. # 7 exempt rewrite by name + nochange a0-1.tld2. # 8 exempt rewrite by address block + addr 12.12.12.12 a4-1.tld2 # 9 prefer QNAME policy to NSDNAME + addr 127.0.0.1 a3-1.sub3.tld2 # 10 prefer policy for largest NSDNAME + addr 127.0.0.2 a3-1.subsub.sub3.tld2 + nxdomain xxx.crash1.tld2 # 12 dns_db_detachnode() crash + end_group else - echo "I:RPZ NSDNAME not checked; named was not built with --enable-rpz-nsdname" + echo "I:NSDNAME not checked; named not configured with --enable-rpz-nsdname" fi if ./rpz nsip; then - start_test "RPZ NSIP rewrites" test4 - nxdomain a3-1.tld2 - nochange . - end_test + start_group "NSIP rewrites" test4 + nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2 by NSIP + nochange a3-2.tld2. # 2 exempt rewrite by name + nochange a0-1.tld2. # 3 exempt rewrite by address block + nochange a3-1.tld4 # 4 different NS IP address + end_group else - echo "I:RPZ NSIP not checked; named was not built with --enable-rpz-nsip" -fi - -start_test "RPZ policy overrides" test5 -nxdomain a3-1.tld2 -nochange a3-2.tld2 -nodata a3-3.tld2 -nxdomain a3-4.tld2 -dignm a3-5.tld2 -tany -$DIGCMD +noauth a3-5.tld2 -tany @$s3 >$DIGNM -if grep CNAME $DIGNM >/dev/null; then : ; else - echo "'policy cname' failed" - ret=1 + echo "I:NSIP not checked; named not configured with --enable-rpz-nsip" fi -end_test - -ret=0 -echo "I:checking RRSIG queries" -# We don't actually care about the query results; the important -# thing is the server handles RRSIG queries okay -$DIGCMD a3-1.tld2 -trrsig @$s3 > /dev/null 2>&1 -$DIGCMD a3-2.tld2 -trrsig @$s3 > /dev/null 2>&1 -$DIGCMD a3-5.tld2 -trrsig @$s3 > /dev/null 2>&1 -$DIGCMD www.redirect -trrsig @$s3 > /dev/null 2>&1 -$DIGCMD www.cname-redirect -trrsig @$s3 > /dev/null 2>&1 - -$RNDC -c ../common/rndc.conf -s $s3 -p 9953 status > /dev/null 2>&1 || ret=1 -if [ $ret != 0 ]; then - echo "I:failed"; - (cd ..; $PERL start.pl --noclean --restart rpz ns3) -fi -status=`expr $status + $ret` - -ret=0 -echo "I:checking SIG queries" -# We don't actually care about the query results; the important -# thing is the server handles SIG queries okay -$DIGCMD a3-1.tld2 -tsig @$s3 > /dev/null 2>&1 -$DIGCMD a3-2.tld2 -tsig @$s3 > /dev/null 2>&1 -$DIGCMD a3-5.tld2 -tsig @$s3 > /dev/null 2>&1 -$DIGCMD www.redirect -tsig @$s3 > /dev/null 2>&1 -$DIGCMD www.cname-redirect -tsig @$s3 > /dev/null 2>&1 - -$RNDC -c ../common/rndc.conf -s $s3 -p 9953 status > /dev/null 2>&1 || ret=1 -if [ $ret != 0 ]; then - echo "I:failed"; - (cd ..; $PERL start.pl --noclean --restart rpz ns3) -fi -status=`expr $status + $ret` - -ret=0 -echo "I:checking ANY queries" -# We don't actually care about the query results; the important -# thing is the server handles SIG queries okay -$DIGCMD a3-1.tld2 -tany @$s3 > /dev/null 2>&1 -$DIGCMD a3-2.tld2 -tany @$s3 > /dev/null 2>&1 -$DIGCMD a3-5.tld2 -tany @$s3 > /dev/null 2>&1 -$DIGCMD www.redirect -tany @$s3 > /dev/null 2>&1 -$DIGCMD www.cname-redirect -tany @$s3 > /dev/null 2>&1 - -$RNDC -c ../common/rndc.conf -s $s3 -p 9953 status > /dev/null 2>&1 || ret=1 -if [ $ret != 0 ]; then - echo "I:failed"; - (cd ..; $PERL start.pl --noclean --restart rpz ns3) -fi -status=`expr $status + $ret` +# policies in ./test5 overridden by response-policy{} in ns3/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 + ;; status: NOERROR, x + a3-7.tld2. 300 IN CNAME txt-only.tld2. + txt-only.tld2. 120 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 +end_group + +# check that miscellaneous bugs are still absent +start_group "crashes" +for Q in RRSIG SIG ANY 'ANY +dnssec'; do + nocrash a3-1.tld2 -t$Q + nocrash a3-2.tld2 -t$Q + nocrash a3-5.tld2 -t$Q + nocrash www.redirect -t$Q + nocrash www.credirect -t$Q +done +end_group -if test "$status" -eq 0; then - rm -f dig.out* +# 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?" fi echo "I:exit status: $status" diff --git a/bin/tests/system/start.pl b/bin/tests/system/start.pl index 1fa4c96de28..5b683f0bc3f 100644 --- a/bin/tests/system/start.pl +++ b/bin/tests/system/start.pl @@ -15,7 +15,7 @@ # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR # PERFORMANCE OF THIS SOFTWARE. -# $Id: start.pl,v 1.27 2011/10/10 23:18:17 smann Exp $ +# $Id: start.pl,v 1.28 2011/10/13 01:32:32 vjs Exp $ # Framework for starting test servers. # Based on the type of server specified, check for port availability, remove @@ -232,14 +232,15 @@ sub start_server { # already been started my $tries = 0; while (!-s $pid_file) { - if (++$tries > 14) { + if (++$tries > 140) { print "I:Couldn't start server $server (pid=$child)\n"; print "R:FAIL\n"; system "kill -9 $child" if ("$child" ne ""); system "$PERL $topdir/stop.pl $testdir"; exit 1; } - sleep 1; + # sleep for 0.1 seconds + select undef,undef,undef,0.1; } # go back to the top level directory diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 2012d1c91ac..d3099eb4ec6 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + BIND 9 Administrator Reference Manual @@ -4682,6 +4682,19 @@ category notify { null; }; + + + RPZ + + + + Information about errors in response policy zone files, + rewritten responses, and at the highest + debug levels, mere rewriting + attempts. + + + @@ -5211,7 +5224,7 @@ 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 | no-op | nxdomain | nodata | cname domain ; } ; + response-policy { zone_name policy given | disabled | passthru | nxdomain | nodata | cname domain ; } ; }; @@ -9322,141 +9335,213 @@ deny-answer-aliases { "example.net"; }; Response Policy Zone (RPZ) Rewriting BIND 9 includes an intentionally limited - mechanism to modify DNS responses for recursive requests - similar to email anti-spam DNS blacklists. - All 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. - + mechanism to modify DNS responses for recursive requests + somewhat similar 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 rules encoded in a response policy zone (RPZ) are applied - only to responses to queries that ask for recursion (RD=1). - RPZs are normal DNS zones containing RRsets - that can be queried normally if allowed. - It is usually best to restrict those queries with something like - allow-query {none; }; or - allow-query { 127.0.0.1; };. - + + 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. + RPZs are ordinary DNS zones containing RRsets + that can be queried normally if allowed. + It is usually best to restrict those queries with something like + allow-query { localhost; };. + - - There are four kinds of RPZ rewrite rules. QNAME rules are - applied to query names in requests and to targets of CNAME - records resolved in the process of generating the response. - The owner name of a QNAME rule is the query name relativized - to the RPZ. - The records in a rewrite rule are usually A, AAAA, or special - CNAMEs, but can be any type except DNAME. - + + There are four kinds of RPZ records, QNAME, IP, NSIP, + and NSDNAME. + QNAME records are applied to 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. + - - IP rules are triggered by addresses in A and AAAA records. - All IP addresses in A or AAAA RRsets are tested and the rule - longest prefix is applied. Ties between rules with equal prefixes - are broken in favor of the first RPZ mentioned in the - response-policy option. - The rule matching the smallest IP address is chosen among equal - prefix rules from a single RPZ. - IP rules are expressed in RRsets with owner names that are - subdomains of rpz-ip and encoding an IP address block, reversed - as in IN-ARPA. - prefix.B.B.B.B with prefix between 1 and 32 and B between 1 and 255 - encodes an IPv4 address. - IPv6 addresses are encoded by with prefix.W.W.W.W.W.W.W.W or - prefix.WORDS.zz.WORDS. The words in the standard IPv6 text - representation are reversed, "::" is replaced with ".zz.", - and ":" becomes ".". - + + 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 + 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. + B4 is the decimal value of the least significant byte of the + IPv4 address as in IN-ADDR.ARPA. + IPv6 addresses are encoded in a format similar to the standard + IPv6 text representation, + prefixlength.W8.W7.W6.W5.W4.W3.W2.W1.rpz-ip. + Each of W8,...,W1 is a one to four digit hexadecimal number + representing 16 bits of the IPv6 address as in the standard text + representation of IPv6 addresses, but reversed as in IN-ADDR.ARPA. + All 8 words must be present except when consecutive + zero words are replaced with .zz. + analogous to double colons (::) in standard IPv6 text encodings. + The prefix length must be between 1 and 128. + - - NSDNAME rules match names in NS RRsets for the response or a - parent. They are encoded as subdomains of rpz-nsdomain relativized - to the RPZ origin name. - + + 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. + They are encoded as subdomains of + rpz-nsdomain relativized + to the RPZ origin name. + - - NSIP rules match IP addresses in A and AAAA RRsets for names of - responsible servers or the names that can be matched by NSDNAME - rules. The are encoded like IP rules except as subdomains of - rpz-nsip. + + 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 + 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: + + Among applicable zones, use the RPZ that appears first + in the response-policy option. + Prefer QNAME to IP to NSDNAME to NSIP policy records + in a single RPZ + Among applicable NSDNAME policy records, prefer the + policy record that matches the lexically smallest name + Among IP or NSIP policy records, prefer the record + with the longest prefix. + Among records with the same prefex length, + prefer the IP or NSIP policy record that matches + the smallest IP address. + - Authority verification issues and variations in authority data in - the current version of BIND 9 can cause - inconsistent results from NSIP and NSDNAME. So they are available + When the processing of a response is restarted to resolve + DNAME or CNAME records and an applicable policy record set has + not been found, + all RPZs are again consulted for the DNAME or CNAME names + and addresses. + + + + Authority verification issues and variations in authority data + can cause inconsistent results for NSIP and NSDNAME policy records. + Glue NS records often differ from authoritative NS records. + So they are available only when BIND is built with the --enable-rpz-nsip or --enable-rpz-nsdname options - on the "configure" command line. - + on the "configure" command line. + - - Four policies can be expressed. - The NXDOMAIN policy causes a NXDOMAIN response - and is expressed with an RRset consisting of a single CNAME - whose target is the root domain (.). - NODATA generates NODATA or ANCOUNT=1 regardless - of query type. - It is expressed with a CNAME whose target is the wildcard - top-level domain (*.). - The NO-OP policy does not change the response - and is used to "poke holes" in policies for larger CIDR blocks or in - zones named later in the response-policy option. - The NO-OP policy is expressed by a CNAME with a target consisting - of the variable part of the owner name, such as "example.com." for - a QNAME rule or "128.1.0.0.127." for an IP rule. - The CNAME policy is used to replace the RRsets - of response. - A and AAAA RRsets are most common and useful to capture - an evil domain in a walled garden, but any valid set of RRsets - is possible. - + + 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. + + A CNAME whose target is the root domain (.) + specifies the NXDOMAIN policy, + which generates an NXDOMAIN response. + A CNAME whose target is the wildcard top-level + domain (*.) specifies the NODATA policy, + 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 (*) + 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 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 + and is most often used to "poke holes" in policies for + CIDR blocks. + + - - All of the policies in an RPZ can be overridden with a - policy clause. - given says "do not override." - no-op says "do nothing" regardless of the policy - in RPZ records. - nxdomain causes all RPZ rules to generate - NXDOMAIN results. - nodata gives nodata. - cname domain causes all RPZ rules to act as if - the consisted of a "cname domain" record. - + + The policies specified in individual records + 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." + 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. + PASSTHRU causes all policy records + to act as if they were CNAME records with targets the variable + part of their owner name. They protect the response from + being changed. + NXDOMAIN causes all RPZ records + to specify NXDOMAIN policies. + NODATA overrides with the + NODATA policy + CNAME domain causes all RPZ + policy records to act as if they were "cname domain" records. + + - - For example, you might use this option statement + + For example, you might use this option statement -response-policy { zone "bl"; }; + response-policy { zone "badlist"; }; and this zone statement -zone "bl" {type master; file "example/bl"; allow-query {none;}; }; + zone "badlist" {type master; file "master/badlist"; allow-query {none;}; }; with this zone file $TTL 1H -@ SOA LOCALHOST. named-mgr.example.com (1 1h 15m 30d 2h) +@ SOA LOCALHOST. named-mgr.example.com (1 1h 15m 30d 2h) + NS LOCALHOST. + +; QNAME policy records. There are no periods (.) after the owner names. +nxdomain.domain.com CNAME . ; NXDOMAIN policy +nodata.domain.com CNAME *. ; NODATA policy +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. + +bzone.domain.com CNAME garden.example.com. -; QNAME rules -nxdomain.domain.com CNAME . -nodata.domain.com CNAME *. -bad.domain.com A 10.0.0.1 - AAAA 2001:2::1 -ok.domain.com CNAME ok.domain.com. -*.badzone.domain.com CNAME garden.example.com. +; redirect x.bzone.domain.com to x.bzone.domain.com.garden.example.com +*.bzone.domain.com CNAME *.garden.example.com. -; IP rules rewriting all answers for 127/8 except 127.0.0.1 -8.0.0.0.127.ip CNAME . -32.1.0.0.127.ip CNAME 32.1.0.0.127. -; NSDNAME and NSIP rules +; 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 + +; NSDNAME and NSIP policy records ns.domain.com.rpz-nsdname CNAME . 48.zz.2.2001.rpz-nsip CNAME . - + diff --git a/lib/dns/db.c b/lib/dns/db.c index 13778873f3e..e7e4bf154f9 100644 --- a/lib/dns/db.c +++ b/lib/dns/db.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: db.c,v 1.98 2011/10/11 00:09:03 each Exp $ */ +/* $Id: db.c,v 1.99 2011/10/13 01:32:33 vjs Exp $ */ /*! \file */ @@ -1017,10 +1017,11 @@ dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) isc_result_t 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_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)); + ardataset, st, query_qname)); } diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index 0c3ebb799c1..48b5a26eab9 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.106 2011/10/11 00:09:03 each Exp $ */ +/* $Id: db.h,v 1.107 2011/10/13 01:32:34 vjs Exp $ */ #ifndef DNS_DB_H #define DNS_DB_H 1 @@ -178,7 +178,8 @@ typedef struct dns_dbmethods { dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version, dns_rdataset_t *ardataset, - dns_rpz_st_t *st); + dns_rpz_st_t *st, + dns_name_t *query_qname); isc_result_t (*findnodeext)(dns_db_t *db, dns_name_t *name, isc_boolean_t create, dns_clientinfomethods_t *methods, @@ -1547,7 +1548,8 @@ dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st); isc_result_t 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_rdataset_t *ardataset, dns_rpz_st_t *st, + dns_name_t *query_qname); /*%< * Search the CDIR block tree of a response policy tree of trees for the best * match to any of the IP addresses in an A or AAAA rdataset. @@ -1560,6 +1562,10 @@ 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/log.h b/lib/dns/include/dns/log.h index fef8bd32ed4..e211a48808d 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: log.h,v 1.45 2009/12/18 22:16:49 each Exp $ */ +/* $Id: log.h,v 1.46 2011/10/13 01:32:34 vjs Exp $ */ /*! \file dns/log.h * \author Principal Authors: DCL */ @@ -42,6 +42,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGCATEGORY_LAME_SERVERS (&dns_categories[9]) #define DNS_LOGCATEGORY_DELEGATION_ONLY (&dns_categories[10]) #define DNS_LOGCATEGORY_EDNS_DISABLED (&dns_categories[11]) +#define DNS_LOGCATEGORY_RPZ (&dns_categories[12]) /* Backwards compatibility. */ #define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index 7fa702b8398..6b0d8077f15 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rpz.h,v 1.3 2011/01/13 04:59:26 tbox Exp $ */ +/* $Id: rpz.h,v 1.4 2011/10/13 01:32:34 vjs Exp $ */ #ifndef DNS_RPZ_H #define DNS_RPZ_H 1 @@ -37,21 +37,24 @@ typedef enum { DNS_RPZ_TYPE_BAD, DNS_RPZ_TYPE_QNAME, DNS_RPZ_TYPE_IP, - DNS_RPZ_TYPE_NSIP, - DNS_RPZ_TYPE_NSDNAME + DNS_RPZ_TYPE_NSDNAME, + DNS_RPZ_TYPE_NSIP } dns_rpz_type_t; /* - * Require DNS_RPZ_POLICY_NO_OP < DNS_RPZ_POLICY_NXDOMAIN < - * DNS_RPZ_POLICY_NODATA < DNS_RPZ_POLICY_CNAME. + * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_NXDOMAIN < + * DNS_RPZ_POLICY_NODATA < DNS_RPZ_POLICY_CNAME to choose among competing + * policies. */ typedef enum { - DNS_RPZ_POLICY_GIVEN = 0, /* 'given': what something else says */ - DNS_RPZ_POLICY_NO_OP = 1, /* 'no-op': do not rewrite */ - DNS_RPZ_POLICY_NXDOMAIN = 2, /* 'nxdomain': answer with NXDOMAIN */ - DNS_RPZ_POLICY_NODATA = 3, /* 'nodata': answer with ANCOUNT=0 */ - DNS_RPZ_POLICY_CNAME = 4, /* 'cname x': answer with x's rrsets */ - DNS_RPZ_POLICY_RECORD = 5, + DNS_RPZ_POLICY_GIVEN = 0, /* 'given': what policy record says */ + DNS_RPZ_POLICY_DISABLED = 1, /* 'cname x': answer with x's rrsets */ + DNS_RPZ_POLICY_PASSTHRU = 2, /* 'passthru': do not rewrite */ + DNS_RPZ_POLICY_NXDOMAIN = 3, /* 'nxdomain': answer with NXDOMAIN */ + DNS_RPZ_POLICY_NODATA = 4, /* 'nodata': answer with ANCOUNT=0 */ + DNS_RPZ_POLICY_CNAME = 5, /* 'cname x': answer with x's rrsets */ + DNS_RPZ_POLICY_RECORD, + DNS_RPZ_POLICY_WILDCNAME, DNS_RPZ_POLICY_MISS, DNS_RPZ_POLICY_ERROR } dns_rpz_policy_t; @@ -65,10 +68,9 @@ struct dns_rpz_zone { ISC_LINK(dns_rpz_zone_t) link; int num; dns_name_t origin; /* Policy zone name */ - dns_name_t nsdname; /* RPZ_NSDNAME_ZONE.origin */ - dns_rpz_policy_t policy; /* RPZ_POLICY_GIVEN or override */ - dns_name_t cname; /* override name for - RPZ_POLICY_CNAME */ + dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */ + dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */ + dns_name_t cname; /* override value for ..._CNAME */ }; /* @@ -82,13 +84,15 @@ typedef struct dns_rpz_cidr dns_rpz_cidr_t; typedef struct { unsigned int state; # define DNS_RPZ_REWRITTEN 0x0001 -# define DNS_RPZ_DONE_QNAME 0x0002 -# define DNS_RPZ_DONE_A 0x0004 -# define DNS_RPZ_RECURSING 0x0008 -# define DNS_RPZ_HAVE_IP 0x0010 -# define DNS_RPZ_HAVE_NSIPv4 0x0020 -# define DNS_RPZ_HAVE_NSIPv6 0x0040 -# define DNS_RPZ_HAD_NSDNAME 0x0080 +# define DNS_RPZ_DONE_QNAME 0x0002 /* qname checked */ +# define DNS_RPZ_DONE_QNAME_IP 0x0004 /* IP addresses of qname checked */ +# define DNS_RPZ_DONE_NSDNAME 0x0008 /* NS name missed; checking addresses */ +# define DNS_RPZ_DONE_IPv4 0x0010 +# define DNS_RPZ_RECURSING 0x0020 +# define DNS_RPZ_HAVE_IP 0x0040 /* a policy zone has IP addresses */ +# define DNS_RPZ_HAVE_NSIPv4 0x0080 /* IPv4 NISP addresses */ +# define DNS_RPZ_HAVE_NSIPv6 0x0100 /* IPv6 NISP addresses */ +# define DNS_RPZ_HAVE_NSDNAME 0x0200 /* NS names */ /* * Best match so far. */ @@ -105,7 +109,7 @@ typedef struct { dns_rdataset_t *rdataset; } m; /* - * State for chasing NS names and addresses including recursion. + * State for chasing IP addresses and NS names including recursion. */ struct { unsigned int label; @@ -114,7 +118,7 @@ typedef struct { dns_rdatatype_t r_type; isc_result_t r_result; dns_rdataset_t *r_rdataset; - } ns; + } r; /* * State of real query while recursing for NSIP or NSDNAME. */ @@ -146,6 +150,7 @@ typedef struct { #define DNS_RPZ_INFO_LEVEL ISC_LOG_INFO #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) const char * dns_rpz_type2str(dns_rpz_type_t type); @@ -153,6 +158,9 @@ dns_rpz_type2str(dns_rpz_type_t type); dns_rpz_policy_t dns_rpz_str2policy(const char *str); +const char * +dns_rpz_policy2str(dns_rpz_policy_t policy); + void dns_rpz_set_need(isc_boolean_t need); diff --git a/lib/dns/log.c b/lib/dns/log.c index 388dd8f79bb..001ca8c995d 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: log.c,v 1.47 2009/12/18 23:49:03 tbox Exp $ */ +/* $Id: log.c,v 1.48 2011/10/13 01:32:33 vjs Exp $ */ /*! \file */ @@ -44,6 +44,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = { { "lame-servers", 0 }, { "delegation-only", 0 }, { "edns-disabled", 0 }, + { "rpz", 0 }, { NULL, 0 } }; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index d95c2b4c882..1571126c8e4 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rbtdb.c,v 1.318 2011/10/12 23:09:35 marka Exp $ */ +/* $Id: rbtdb.c,v 1.319 2011/10/13 01:32:33 vjs Exp $ */ /*! \file */ @@ -4588,15 +4588,19 @@ get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st) * Search the CDIR block tree of a response policy tree of trees for all of * the IP addresses in an A or AAAA rdataset. * Among the policies for all IPv4 and IPv6 addresses for a name, choose - * the longest prefix. Among those with the longest prefix, the first - * configured policy. Among answers for with the longest prefixes for - * two or more IP addresses in the A and AAAA rdatasets the lexically - * smallest address. + * the earliest configured policy, + * QNAME over IP over NSDNAME over NSIP, + * the longest prefix, + * the lexically smallest address. + * The caller must have already checked that any existing policy was not + * configured earlier than this policy zone and does not have a higher + * precedence type. */ static isc_result_t 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_rdataset_t *ardataset, dns_rpz_st_t *st, + dns_name_t *query_qname) { dns_rbtdb_t *rbtdb; struct in_addr ina; @@ -4617,8 +4621,6 @@ 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); - dns_db_detach(&db); - dns_zone_detach(&zone); return (ISC_R_UNEXPECTED); } @@ -4653,17 +4655,19 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, continue; /* - * Choose the policy with the longest matching prefix. - * Between policies with the same prefix, choose the first - * configured. + * If we already have a rule, discard this new rule if + * is not better. + * The caller has checked that st->m.rpz->num > rpz->num + * or st->m.rpz->num == rpz->num and st->m.type >= rpz_type */ - if (st->m.policy != DNS_RPZ_POLICY_MISS) { - if (prefix < st->m.prefix) - continue; - if (prefix == st->m.prefix && - rpz->num > st->m.rpz->num) - continue; - } + if (st->m.policy != DNS_RPZ_POLICY_MISS && + st->m.rpz->num == rpz->num && + (st->m.type < rpz_type || + (st->m.type == rpz_type && + (st->m.prefix > prefix || + (st->m.prefix == prefix && + 0 > dns_name_rdatacompare(st->qname, qname)))))) + continue; /* * We have rpz_st an entry with a prefix at least as long as @@ -4677,8 +4681,8 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, char namebuf[DNS_NAME_FORMATSIZE]; dns_name_format(qname, namebuf, sizeof(namebuf)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, - DNS_LOGMODULE_CACHE, DNS_RPZ_ERROR_LEVEL, + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, + DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, "rpz_findips findnode(%s): %s", namebuf, isc_result_totext(result)); continue; @@ -4702,7 +4706,8 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, } else { rpz_policy = dns_rpz_decode_cname(&zrdataset, selfname); - if (rpz_policy == DNS_RPZ_POLICY_RECORD) + if (rpz_policy == DNS_RPZ_POLICY_RECORD || + rpz_policy == DNS_RPZ_POLICY_WILDCNAME) result = DNS_R_CNAME; } ttl = zrdataset.ttl; @@ -4715,44 +4720,59 @@ rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, /* * Use an overriding action specified in the configuration file */ - if (rpz->policy != DNS_RPZ_POLICY_GIVEN && - rpz_policy != DNS_RPZ_POLICY_NO_OP) + if (rpz->policy != DNS_RPZ_POLICY_GIVEN) { + /* + * only log DNS_RPZ_POLICY_DISABLED hits + */ + if (rpz->policy == DNS_RPZ_POLICY_DISABLED) { + if (isc_log_wouldlog(dns_lctx, + DNS_RPZ_INFO_LEVEL)) { + char qname_buf[DNS_NAME_FORMATSIZE]; + char rpz_qname_buf[DNS_NAME_FORMATSIZE]; + dns_name_format(query_qname, qname_buf, + sizeof(qname_buf)); + dns_name_format(qname, rpz_qname_buf, + sizeof(rpz_qname_buf)); + + isc_log_write(dns_lctx, + DNS_LOGCATEGORY_RPZ, + DNS_LOGMODULE_RBTDB, + DNS_RPZ_INFO_LEVEL, + "disabled rpz %s %s rewrite" + " %s via %s", + dns_rpz_type2str(rpz_type), + dns_rpz_policy2str(rpz_policy), + qname_buf, rpz_qname_buf); + } + continue; + } + rpz_policy = rpz->policy; + } - /* - * We know the new prefix is at least as long as the current. - * Prefer the new answer if the new prefix is longer. - * Prefer the zone configured first if the prefixes are equal. - * With two actions from the same zone, prefer the action - * on the "smallest" name. - */ - if (st->m.policy == DNS_RPZ_POLICY_MISS || - prefix > st->m.prefix || - rpz->num <= st->m.rpz->num || - 0 > dns_name_compare(qname, st->qname)) { - if (dns_rdataset_isassociated(st->m.rdataset)) - dns_rdataset_disassociate(st->m.rdataset); - if (st->m.node != NULL) - dns_db_detachnode(st->m.db, &st->m.node); - if (st->m.db != NULL) - dns_db_detach(&st->m.db); - if (st->m.zone != NULL) - dns_zone_detach(&st->m.zone); - st->m.rpz = rpz; - st->m.type = rpz_type; - st->m.prefix = prefix; - st->m.policy = rpz_policy; - st->m.ttl = ttl; - st->m.result = result; - dns_name_copy(qname, st->qname, NULL); - if (rpz_policy == DNS_RPZ_POLICY_RECORD && - result != DNS_R_NXRRSET) { - dns_rdataset_clone(&zrdataset,st->m.rdataset); - dns_db_attachnode(db, node, &st->m.node); - } - dns_db_attach(db, &st->m.db); - dns_zone_attach(zone, &st->m.zone); + if (dns_rdataset_isassociated(st->m.rdataset)) + dns_rdataset_disassociate(st->m.rdataset); + if (st->m.node != NULL) + dns_db_detachnode(st->m.db, &st->m.node); + if (st->m.db != NULL) + dns_db_detach(&st->m.db); + if (st->m.zone != NULL) + dns_zone_detach(&st->m.zone); + st->m.rpz = rpz; + st->m.type = rpz_type; + st->m.prefix = prefix; + st->m.policy = rpz_policy; + st->m.ttl = ttl; + st->m.result = result; + dns_name_copy(qname, st->qname, NULL); + if ((rpz_policy == DNS_RPZ_POLICY_RECORD || + rpz_policy == DNS_RPZ_POLICY_WILDCNAME) && + result != DNS_R_NXRRSET) { + dns_rdataset_clone(&zrdataset,st->m.rdataset); + dns_db_attachnode(db, node, &st->m.node); } + dns_db_attach(db, &st->m.db); + dns_zone_attach(zone, &st->m.zone); if (dns_rdataset_isassociated(&zrdataset)) dns_rdataset_disassociate(&zrdataset); } diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index d3f990a38a5..b41f1fe0578 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -14,7 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rpz.c,v 1.7 2011/01/17 04:27:23 marka Exp $ */ +/* $Id: rpz.c,v 1.8 2011/10/13 01:32:34 vjs Exp $ */ /*! \file */ @@ -116,20 +116,17 @@ struct dns_rpz_cidr_node { struct dns_rpz_cidr { isc_mem_t *mctx; - isc_boolean_t had_nsdname; + 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 ip_name; /* RPZ_IP_ZONE.LOCALHOST. */ dns_name_t nsip_name; /* RPZ_NSIP_ZONE.LOCALHOST. */ dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.LOCALHOST */ }; - static isc_boolean_t have_rpz_zones = ISC_FALSE; - const char * -dns_rpz_type2str(dns_rpz_type_t type) -{ +dns_rpz_type2str(dns_rpz_type_t type) { switch (type) { case DNS_RPZ_TYPE_QNAME: return ("QNAME"); @@ -143,31 +140,61 @@ dns_rpz_type2str(dns_rpz_type_t type) break; } FATAL_ERROR(__FILE__, __LINE__, - "impossible response policy zone type %d", type); + "impossible rpz type %d", type); return ("impossible"); } - - dns_rpz_policy_t -dns_rpz_str2policy(const char *str) -{ +dns_rpz_str2policy(const char *str) { if (str == NULL) return (DNS_RPZ_POLICY_ERROR); if (!strcasecmp(str, "given")) return (DNS_RPZ_POLICY_GIVEN); - if (!strcasecmp(str, "no-op")) - return (DNS_RPZ_POLICY_NO_OP); + if (!strcasecmp(str, "disabled")) + return (DNS_RPZ_POLICY_DISABLED); + if (!strcasecmp(str, "passthru")) + return (DNS_RPZ_POLICY_PASSTHRU); if (!strcasecmp(str, "nxdomain")) return (DNS_RPZ_POLICY_NXDOMAIN); if (!strcasecmp(str, "nodata")) return (DNS_RPZ_POLICY_NODATA); if (!strcasecmp(str, "cname")) return (DNS_RPZ_POLICY_CNAME); + /* + * Obsolete + */ + if (!strcasecmp(str, "no-op")) + return (DNS_RPZ_POLICY_PASSTHRU); return (DNS_RPZ_POLICY_ERROR); } +const char * +dns_rpz_policy2str(dns_rpz_policy_t policy) { + const char *str; + switch (policy) { + case DNS_RPZ_POLICY_PASSTHRU: + str = "PASSTHRU"; + break; + case DNS_RPZ_POLICY_NXDOMAIN: + str = "NXDOMAIN"; + break; + case DNS_RPZ_POLICY_NODATA: + str = "NODATA"; + break; + case DNS_RPZ_POLICY_RECORD: + str = "records"; + break; + case DNS_RPZ_POLICY_CNAME: + case DNS_RPZ_POLICY_WILDCNAME: + str = "CNAME"; + break; + default: + str = ""; + INSIST(0); + } + return (str); +} /* * Free the radix tree of a response policy database. @@ -214,8 +241,6 @@ dns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) { *cidrp = NULL; } - - /* * Forget a view's list of policy zones. */ @@ -244,20 +269,15 @@ dns_rpz_view_destroy(dns_view_t *view) { * zone is in at least one view's list of policy zones. */ void -dns_rpz_set_need(isc_boolean_t need) -{ +dns_rpz_set_need(isc_boolean_t need) { have_rpz_zones = need; } - isc_boolean_t -dns_rpz_needed(void) -{ +dns_rpz_needed(void) { return (have_rpz_zones); } - - /* * Start a new radix tree for a response policy zone. */ @@ -313,12 +333,13 @@ dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin, return (ISC_R_SUCCESS); } - /* * See if a policy zone has IP, NSIP, or NSDNAME rules or records. */ void dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) { + if (cidr == NULL) + return; if (cidr->root != NULL && (cidr->root->flags & DNS_RPZ_CIDR_FG_IP) != 0) st->state |= DNS_RPZ_HAVE_IP; @@ -328,8 +349,8 @@ dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) { if (cidr->root != NULL && (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0) st->state |= DNS_RPZ_HAVE_NSIPv6; - if (cidr->had_nsdname) - st->state |= DNS_RPZ_HAD_NSDNAME; + if (cidr->have_nsdname) + st->state |= DNS_RPZ_HAVE_NSDNAME; } static inline dns_rpz_cidr_flags_t @@ -350,8 +371,6 @@ get_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix, } } - - /* * Mark a node as having IP or NSIP data and all of its parents * as members of the IP or NSIP tree. @@ -371,8 +390,6 @@ set_node_flags(dns_rpz_cidr_node_t *node, dns_rpz_type_t rpz_type) { } } - - /* * Make a radix tree node. */ @@ -409,24 +426,18 @@ new_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip, return (node); } - - static void -badname(int level, dns_name_t *name, const char *comment) -{ +badname(int level, dns_name_t *name, const char *comment) { char printname[DNS_NAME_FORMATSIZE]; if (isc_log_wouldlog(dns_lctx, level)) { dns_name_format(name, printname, sizeof(printname)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, level, - "invalid response policy name \"%s\"%s", - printname, comment); + "invalid rpz \"%s\"%s", printname, comment); } } - - /* * Convert an IP address from radix tree binary (host byte order) to * to its canonical response policy domain name and its name in the @@ -520,8 +531,6 @@ ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, return (ISC_R_SUCCESS); } - - /* * Decide which kind of IP address response policy zone a name is in. */ @@ -548,8 +557,6 @@ set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) { return (DNS_RPZ_TYPE_QNAME); } - - /* * Convert an IP address from canonical response policy domain name form * to radix tree binary (host byte order). @@ -695,26 +702,37 @@ name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, return (ISC_R_SUCCESS); } - - /* - * find first differing bit + * Find first differing bit. */ static int ffbit(dns_rpz_cidr_word_t w) { int bit; - if (w == 0) - return (DNS_RPZ_CIDR_WORD_BITS); - for (bit = 0; (w & (1U << (DNS_RPZ_CIDR_WORD_BITS-1))) == 0; bit++) - w <<= 1; + bit = DNS_RPZ_CIDR_WORD_BITS-1; + if ((w & 0xffff0000) != 0) { + w >>= 16; + bit -= 16; + } + if ((w & 0xff00) != 0) { + w >>= 8; + bit -= 8; + } + if ((w & 0xf0) != 0) { + w >>= 4; + bit -= 4; + } + if ((w & 0xc) != 0) { + w >>= 2; + bit -= 2; + } + if ((w & 2) != 0) + --bit; return (bit); } - - /* - * find the first differing bit in two keys + * Find the first differing bit in two keys. */ static int diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1, @@ -741,14 +759,12 @@ diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1, return (ISC_MIN(bit, maxbit)); } - - /* * Search a radix tree for an IP address for ordinary lookup * or for a CIDR block adding or deleting an entry * The tree read (for simple search) or write lock must be held by the caller. * - * return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS, + * Return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS, * ISC_R_NOMEMORY */ static isc_result_t @@ -912,15 +928,12 @@ search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, } } - - /* * Add an IP address to the radix tree of a response policy database. * The tree write lock must be held by the caller. */ void -dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) -{ +dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) { dns_rpz_cidr_key_t tgt_ip; dns_rpz_cidr_bits_t tgt_prefix; dns_rpz_type_t type; @@ -929,7 +942,7 @@ dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) return; /* - * no worries if the new name is not an IP address + * No worries if the new name is not an IP address. */ type = set_type(cidr, name); switch (type) { @@ -937,7 +950,7 @@ dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) case DNS_RPZ_TYPE_NSIP: break; case DNS_RPZ_TYPE_NSDNAME: - cidr->had_nsdname = ISC_TRUE; + cidr->have_nsdname = ISC_TRUE; return; case DNS_RPZ_TYPE_QNAME: case DNS_RPZ_TYPE_BAD: @@ -953,15 +966,12 @@ dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) char printname[DNS_NAME_FORMATSIZE]; dns_name_format(name, printname, sizeof(printname)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "duplicate response policy name \"%s\"", - printname); + "duplicate rpz name \"%s\"", printname); } } - - /* * Delete an IP address from the radix tree of a response policy database. * The tree write lock must be held by the caller. @@ -1000,7 +1010,7 @@ 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_LEVEL2, name, + if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_DEBUG_LEVEL3, name, type, &tgt_ip, &tgt_prefix)) return; if (ISC_R_SUCCESS != search(cidr, &tgt_ip, tgt_prefix, type, @@ -1009,10 +1019,9 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { char printname[DNS_NAME_FORMATSIZE]; dns_name_format(name, printname, sizeof(printname)); - isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, - "missing response policy node \"%s\"", - printname); + "missing rpz node \"%s\"", printname); } return; } @@ -1073,8 +1082,6 @@ dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { } while (tgt != NULL); } - - /* * Caller must hold tree lock. * Return ISC_R_NOTFOUND @@ -1124,8 +1131,6 @@ dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr, canon_name, search_name)); } - - /* * Translate CNAME rdata to a QNAME response policy action. */ @@ -1148,21 +1153,31 @@ dns_rpz_decode_cname(dns_rdataset_t *rdataset, dns_name_t *selfname) { if (dns_name_equal(&cname.cname, dns_rootname)) return (DNS_RPZ_POLICY_NXDOMAIN); - /* - * CNAME *. means NODATA - */ - if (dns_name_countlabels(&cname.cname) == 2 - && dns_name_iswildcard(&cname.cname)) - return (DNS_RPZ_POLICY_NODATA); + if (dns_name_iswildcard(&cname.cname)) { + /* + * CNAME *. means NODATA + */ + if (dns_name_countlabels(&cname.cname) == 2) + return (DNS_RPZ_POLICY_NODATA); + + /* + * A qname of www.evil.com and a policy of + * *.evil.com CNAME *.garden.net + * gives a result of + * evil.com CNAME evil.com.garden.net + */ + if (dns_name_countlabels(&cname.cname) > 2) + return (DNS_RPZ_POLICY_WILDCNAME); + } /* * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. means "do not rewrite" */ if (selfname != NULL && dns_name_equal(&cname.cname, selfname)) - return (DNS_RPZ_POLICY_NO_OP); + return (DNS_RPZ_POLICY_PASSTHRU); /* - * evil.com CNAME garden.net rewrites www.evil.com to www.garden.net. + * Any other rdata gives a response consisting of the rdata. */ return (DNS_RPZ_POLICY_RECORD); } diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 62fd560b497..a5f9dc50f5a 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.141 2011/09/06 22:29:33 smann Exp $ */ +/* $Id: namedconf.c,v 1.142 2011/10/13 01:32:34 vjs Exp $ */ /*! \file */ @@ -1027,7 +1027,8 @@ static cfg_type_t cfg_type_masterformat = { /* * response-policy { - * zone [ policy (given|no-op|nxdomain|nodata|cname ) ]; + * zone [ policy (given|disabled|passthru| + * nxdomain|nodata|cname ) ]; * }; * * this is a chimera of doc_optional_keyvalue() and cfg_doc_enum() @@ -1095,7 +1096,8 @@ cleanup: } static const char *rpz_policies[] = { - "given", "no-op", "nxdomain", "nodata", "cname", NULL + "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,