+3495. [func] Support multiple response-policy zones, while
+ improving RPZ performance. [RT #32476]
+
3494. [func] DNS RRL: Blunt the impact of DNS reflection and
amplification attacks by rate-limiting substantially-
identical responses. [RT #28130]
dns_nsstatscounter_ratedropped = 38,
dns_nsstatscounter_rateslipped = 39,
- dns_nsstatscounter_max = 40
+ dns_nsstatscounter_rpz_rewrites = 40,
+
+ dns_nsstatscounter_max = 41
};
void
}
static void
-rpz_log_rewrite(ns_client_t *client, const char *disabled,
+rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
dns_rpz_policy_t policy, dns_rpz_type_t type,
- dns_name_t *rpz_qname) {
+ dns_zone_t *p_zone, dns_name_t *p_name)
+{
+ isc_stats_t *zonestats;
char qname_buf[DNS_NAME_FORMATSIZE];
- char rpz_qname_buf[DNS_NAME_FORMATSIZE];
+ char p_name_buf[DNS_NAME_FORMATSIZE];
+
+ /*
+ * Count enabled rewrites in the global counter.
+ * Count both enabled and disabled rewrites for each zone.
+ */
+ if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
+ if (p_zone != NULL) {
+ zonestats = dns_zone_getrequeststats(p_zone);
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
return;
dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
- dns_name_format(rpz_qname, rpz_qname_buf, sizeof(rpz_qname_buf));
+ dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s",
- disabled,
+ disabled ? "disabled " : "",
dns_rpz_type2str(type), dns_rpz_policy2str(policy),
- qname_buf, rpz_qname_buf);
+ qname_buf, p_name_buf);
}
static void
-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)
+rpz_log_fail(ns_client_t *client, int level, dns_name_t *p_name,
+ dns_rpz_type_t rpz_type, const char *str, isc_result_t result)
{
- char namebuf1[DNS_NAME_FORMATSIZE];
- char namebuf2[DNS_NAME_FORMATSIZE];
+ char qnamebuf[DNS_NAME_FORMATSIZE];
+ char p_namebuf[DNS_NAME_FORMATSIZE];
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));
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
+ dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
NS_LOGMODULE_QUERY, level,
"rpz %s rewrite %s via %s %sfailed: %s",
dns_rpz_type2str(rpz_type),
- namebuf1, namebuf2, str, isc_result_totext(result));
+ qnamebuf, p_namebuf, str, isc_result_totext(result));
}
/*
* 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,
+rpz_getdb(ns_client_t *client, dns_name_t *p_name, dns_rpz_type_t rpz_type,
dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
{
- char namebuf1[DNS_NAME_FORMATSIZE];
- char namebuf2[DNS_NAME_FORMATSIZE];
+ char qnamebuf[DNS_NAME_FORMATSIZE];
+ char p_namebuf[DNS_NAME_FORMATSIZE];
dns_dbversion_t *rpz_version = NULL;
isc_result_t result;
- result = query_getzonedb(client, rpz_qname, dns_rdatatype_any,
+ result = query_getzonedb(client, p_name, dns_rdatatype_any,
DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
if (result == ISC_R_SUCCESS) {
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));
+ dns_name_format(client->query.qname, qnamebuf,
+ sizeof(qnamebuf));
+ dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
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),
- namebuf1, namebuf2);
+ qnamebuf, p_namebuf);
}
*versionp = rpz_version;
return (ISC_R_SUCCESS);
}
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname,
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
"query_getzonedb() ", result);
return (result);
}
dns_rdataset_disassociate(*rdatasetp);
}
-static void
+static inline void
rpz_match_clear(dns_rpz_st_t *st)
{
rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
}
static inline isc_result_t
-rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp,
- dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp)
+rpz_ready(ns_client_t *client, dns_rdataset_t **rdatasetp)
{
REQUIRE(rdatasetp != NULL);
- rpz_clean(zonep, dbp, nodep, rdatasetp);
if (*rdatasetp == NULL) {
*rdatasetp = query_newrdataset(client);
if (*rdatasetp == NULL)
return (DNS_R_SERVFAIL);
+ } else if (dns_rdataset_isassociated(*rdatasetp)) {
+ dns_rdataset_disassociate(*rdatasetp);
}
return (ISC_R_SUCCESS);
}
st->m.policy = DNS_RPZ_POLICY_MISS;
}
+static dns_rpz_zbits_t
+rpz_get_zbits(ns_client_t *client,
+ dns_rdatatype_t ip_type, dns_rpz_type_t rpz_type)
+{
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_st_t *st;
+ dns_rpz_zbits_t zbits;
+
+ rpzs = client->view->rpzs;
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ zbits = rpzs->have.qname;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ if (ip_type == dns_rdatatype_a) {
+ zbits = rpzs->have.ipv4;
+ } else if (ip_type == dns_rdatatype_aaaa) {
+ zbits = rpzs->have.ipv6;
+ } else {
+ zbits = rpzs->have.ip;
+ }
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ zbits = rpzs->have.nsdname;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ if (ip_type == dns_rdatatype_a) {
+ zbits = rpzs->have.nsipv4;
+ } else if (ip_type == dns_rdatatype_aaaa) {
+ zbits = rpzs->have.nsipv6;
+ } else {
+ zbits = rpzs->have.nsip;
+ }
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ st = client->query.rpz_st;
+
+ /*
+ * Choose
+ * the earliest configured policy zone (rpz->num)
+ * QNAME over IP over NSDNAME over NSIP (rpz_type)
+ * the smallest name,
+ * the longest IP address prefix,
+ * the lexically smallest address.
+ */
+ if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+ if (st->m.type >= rpz_type) {
+ zbits &= DNS_RPZ_ZMASK(st->m.rpz->num);
+ } else{
+ zbits &= DNS_RPZ_ZMASK(st->m.rpz->num) >> 1;
+ }
+ }
+
+ /*
+ * If the client wants recursion, allow only compatible policies.
+ */
+ if (!RECURSIONOK(client))
+ zbits &= rpzs->p.no_rd_ok;
+
+ return (zbits);
+}
+
/*
- * Get NS, A, or AAAA rrset for response policy zone checks.
+ * Get an NS, A, or AAAA rrset related to the response for the client
+ * to check the contents of that rrset for hits by eligible policy zones.
*/
static isc_result_t
-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,
+rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
+ dns_rpz_type_t rpz_type, dns_db_t **dbp, dns_dbversion_t *version,
dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
{
dns_rpz_st_t *st;
dns_clientinfomethods_t cm;
dns_clientinfo_t ci;
- dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client);
-
st = client->query.rpz_st;
if ((st->state & DNS_RPZ_RECURSING) != 0) {
INSIST(st->r.r_type == type);
INSIST(dns_name_equal(name, st->r_name));
INSIST(*rdatasetp == NULL ||
!dns_rdataset_isassociated(*rdatasetp));
+ INSIST(*dbp == NULL);
st->state &= ~DNS_RPZ_RECURSING;
*dbp = st->r.db;
st->r.db = NULL;
st->r.r_rdataset = NULL;
result = st->r.r_result;
if (result == DNS_R_DELEGATION) {
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
- rpz_type, name,
- "rpz_rrset_find(1) ", result);
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, "rpz_rrset_find(1) ", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
result = DNS_R_SERVFAIL;
}
return (result);
}
- result = rpz_ready(client, NULL, NULL, NULL, rdatasetp);
+ result = rpz_ready(client, rdatasetp);
if (result != ISC_R_SUCCESS) {
st->m.policy = DNS_RPZ_POLICY_ERROR;
return (result);
result = query_getdb(client, name, type, 0, &zone, dbp,
&version, &is_zone);
if (result != ISC_R_SUCCESS) {
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
- rpz_type, name,
- "rpz_rrset_find(2) ", result);
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, "rpz_rrset_find(2) ", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
if (zone != NULL)
dns_zone_detach(&zone);
node = NULL;
dns_fixedname_init(&fixed);
found = dns_fixedname_name(&fixed);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client);
result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK,
client->now, &node, found,
&cm, &ci, *rdatasetp, NULL);
}
/*
- * Check the IP address in an A or AAAA rdataset against
- * the IP or NSIP response policy rules of a view.
+ * Compute a policy owner name, p_name, in a policy zone given the needed
+ * policy type and the trigger name.
*/
static isc_result_t
-rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset,
- dns_rpz_type_t rpz_type)
-{
- dns_rpz_st_t *st;
- dns_dbversion_t *version;
- dns_zone_t *zone;
- dns_db_t *db;
- dns_rpz_zone_t *rpz;
- isc_result_t result;
-
- st = client->query.rpz_st;
- if (st->m.rdataset == NULL) {
- st->m.rdataset = query_newrdataset(client);
- if (st->m.rdataset == NULL)
- return (DNS_R_SERVFAIL);
- }
- zone = NULL;
- db = NULL;
- for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
- rpz != NULL;
- rpz = ISC_LIST_NEXT(rpz, link)) {
- if (!RECURSIONOK(client) && rpz->recursive_only)
- continue;
-
- /*
- * Do not check policy zones that cannot replace a policy
- * already known to match.
- */
- 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.
- */
- version = NULL;
- result = rpz_getdb(client, rpz_type, &rpz->origin,
- &zone, &db, &version);
- if (result != ISC_R_SUCCESS) {
- rpz_clean(&zone, &db, NULL, NULL);
- continue;
- }
- /*
- * Look for a better (e.g. longer prefix) hit for an IP address
- * in this rdataset in this radix tree than than the previous
- * hit, if any. Note the domain name and quality of the
- * best hit.
- */
- 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_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)
+rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
+ dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_name_t *trig_name)
{
+ dns_offsets_t prefix_offsets;
+ dns_name_t prefix, *suffix;
+ unsigned int first, labels;
isc_result_t result;
- 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, rpz_type);
+ /*
+ * The policy owner name consists of a suffix depending on the type
+ * and policy zone and a prefix that is the longest possible string
+ * from the trigger name that keesp the resulting policy owner name
+ * from being too long.
+ */
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ suffix = &rpz->origin;
break;
- case DNS_R_EMPTYNAME:
- case DNS_R_EMPTYWILD:
- case DNS_R_NXDOMAIN:
- case DNS_R_NCACHENXDOMAIN:
- case DNS_R_NXRRSET:
- case DNS_R_NCACHENXRRSET:
- case ISC_R_NOTFOUND:
- result = ISC_R_SUCCESS;
+ case DNS_RPZ_TYPE_IP:
+ suffix = &rpz->ip;
break;
- case DNS_R_DELEGATION:
- case DNS_R_DUPLICATE:
- case DNS_R_DROP:
+ case DNS_RPZ_TYPE_NSDNAME:
+ suffix = &rpz->nsdname;
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;
+ case DNS_RPZ_TYPE_NSIP:
+ suffix = &rpz->nsip;
break;
default:
- if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
- client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
- name, "NS address rewrite rrset ", result);
- }
- break;
+ INSIST(0);
}
- 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);
+ /*
+ * Start with relative version of the full trigger name,
+ * and trim enough allow the addition of the suffix.
+ */
+ dns_name_init(&prefix, prefix_offsets);
+ labels = dns_name_countlabels(trig_name);
+ first = 0;
+ for (;;) {
+ dns_name_getlabelsequence(trig_name, first, labels-first-1,
+ &prefix);
+ result = dns_name_concatenate(&prefix, suffix, p_name, NULL);
if (result == ISC_R_SUCCESS)
- st->state |= DNS_RPZ_DONE_IPv4;
- } else {
- result = ISC_R_SUCCESS;
+ return (ISC_R_SUCCESS);
+ INSIST(result == DNS_R_NAMETOOLONG);
+ /*
+ * Trim the trigger name until the combination is not too long.
+ */
+ if (labels-first < 2) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, suffix,
+ rpz_type, "concatentate() ", result);
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * Complain once about trimming the trigger name.
+ */
+ if (first == 0) {
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, suffix,
+ rpz_type, "concatentate() ", result);
+ }
+ ++first;
}
- 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.
+ * Look in policy zone rpz for a policy of rpz_type by p_name.
+ * The self- name (usually the client qname or an NS name) is compared with
+ * the target of a CNAME policy for the old style passthru encoding.
+ * If found, the policy is recorded in *zonep, *dbp, *versionp, *nodep,
+ * *rdatasetp, and *policyp.
+ * The target DNS type, qtype, chooses the best rdataset for *rdatasetp.
+ * The caller must decide if the found policy is most suitable, including
+ * better than a previously found policy.
+ * If it is best, the caller records it in client->query.rpz_st->m.
*/
static isc_result_t
-rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
- dns_name_t *sname, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
- dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
- dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
- dns_rpz_policy_t *policyp)
+rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
+ dns_name_t *p_name, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+ dns_rpz_policy_t *policyp)
{
- dns_rpz_policy_t policy;
- dns_fixedname_t fixed;
+ dns_fixedname_t foundf;
dns_name_t *found;
isc_result_t result;
dns_clientinfomethods_t cm;
REQUIRE(nodep != NULL);
- dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client);
-
- result = rpz_ready(client, zonep, dbp, nodep, rdatasetp);
- if (result != ISC_R_SUCCESS) {
- *policyp = DNS_RPZ_POLICY_ERROR;
- return (result);
- }
-
/*
- * Try to get either a CNAME or the type of record demanded by the
+ * Try to find either a CNAME or the type of record demanded by the
* request from the policy zone.
*/
+ rpz_clean(zonep, dbp, nodep, rdatasetp);
+ result = rpz_ready(client, rdatasetp);
+ if (result != ISC_R_SUCCESS)
+ return (DNS_R_SERVFAIL);
*versionp = NULL;
- result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, versionp);
- if (result != ISC_R_SUCCESS) {
- *policyp = DNS_RPZ_POLICY_MISS;
+ result = rpz_getdb(client, p_name, rpz_type, zonep, dbp, versionp);
+ if (result != ISC_R_SUCCESS)
return (DNS_R_NXDOMAIN);
- }
-
- dns_fixedname_init(&fixed);
- found = dns_fixedname_name(&fixed);
- result = dns_db_findext(*dbp, qnamef, *versionp, dns_rdatatype_any, 0,
+ dns_fixedname_init(&foundf);
+ found = dns_fixedname_name(&foundf);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client);
+ result = dns_db_findext(*dbp, p_name, *versionp, dns_rdatatype_any, 0,
client->now, nodep, found, &cm, &ci,
*rdatasetp, NULL);
+ /*
+ * Choose the best rdataset if we found something.
+ */
if (result == ISC_R_SUCCESS) {
dns_rdatasetiter_t *rdsiter;
result = dns_db_allrdatasets(*dbp, *nodep, *versionp, 0,
&rdsiter);
if (result != ISC_R_SUCCESS) {
- dns_db_detachnode(*dbp, nodep);
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
- qnamef, "allrdatasets() ", result);
- *policyp = DNS_RPZ_POLICY_ERROR;
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name,
+ rpz_type, "allrdatasets() ", result);
return (DNS_R_SERVFAIL);
}
for (result = dns_rdatasetiter_first(rdsiter);
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_NOMORE) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
- rpz_type, qnamef, "rdatasetiter ",
- result);
- *policyp = DNS_RPZ_POLICY_ERROR;
+ p_name, rpz_type,
+ "rdatasetiter ", result);
return (DNS_R_SERVFAIL);
}
/*
qtype == dns_rdatatype_sig)
result = DNS_R_NXRRSET;
else
- result = dns_db_findext(*dbp, qnamef, *versionp,
+ result = dns_db_findext(*dbp, p_name, *versionp,
qtype, 0, client->now,
nodep, found, &cm, &ci,
*rdatasetp, NULL);
switch (result) {
case ISC_R_SUCCESS:
if ((*rdatasetp)->type != dns_rdatatype_cname) {
- policy = DNS_RPZ_POLICY_RECORD;
+ *policyp = DNS_RPZ_POLICY_RECORD;
} else {
- policy = dns_rpz_decode_cname(rpz, *rdatasetp, sname);
- if ((policy == DNS_RPZ_POLICY_RECORD ||
- policy == DNS_RPZ_POLICY_WILDCNAME) &&
+ *policyp = dns_rpz_decode_cname(rpz, *rdatasetp,
+ self_name);
+ if ((*policyp == DNS_RPZ_POLICY_RECORD ||
+ *policyp == DNS_RPZ_POLICY_WILDCNAME) &&
qtype != dns_rdatatype_cname &&
qtype != dns_rdatatype_any)
- result = DNS_R_CNAME;
+ return (DNS_R_CNAME);
}
- break;
+ return (ISC_R_SUCCESS);
+ case DNS_R_NXRRSET:
+ *policyp = DNS_RPZ_POLICY_NODATA;
+ return (result);
case DNS_R_DNAME:
/*
* DNAME policy RRs have very few if any uses that are not
- * better served with simple wildcards. Making the work would
+ * better served with simple wildcards. Making them work would
* require complications to get the number of labels matched
* 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.
+ * in query_find(). The domain also does not appear in the
+ * summary database at the right level, so this happens only
+ * with a single policy zone when we have no summary database.
+ * Treat it as a miss.
*/
- case DNS_R_NXRRSET:
- policy = DNS_RPZ_POLICY_NODATA;
- break;
case DNS_R_NXDOMAIN:
case DNS_R_EMPTYNAME:
+ return (DNS_R_NXDOMAIN);
+ default:
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
+ "", result);
+ return (DNS_R_SERVFAIL);
+ }
+}
+
+static void
+rpz_save_p(dns_rpz_st_t *st, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_rpz_policy_t policy, dns_name_t *p_name, dns_rpz_prefix_t prefix,
+ isc_result_t result, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+ dns_dbversion_t *version)
+{
+ dns_rdataset_t *trdataset;
+
+ rpz_match_clear(st);
+ st->m.rpz = rpz;
+ st->m.type = rpz_type;
+ st->m.policy = policy;
+ dns_name_copy(p_name, st->p_name, NULL);
+ st->m.prefix = prefix;
+ st->m.result = result;
+ st->m.zone = *zonep;
+ *zonep = NULL;
+ st->m.db = *dbp;
+ *dbp = NULL;
+ st->m.node = *nodep;
+ *nodep = NULL;
+ if (*rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) {
/*
- * If we don't get a qname hit,
- * see if it is worth looking for other types.
+ * Save the replacement rdataset from the policy
+ * and make the previous replacement rdataset scratch.
*/
- dns_db_rpz_enabled(*dbp, client->query.rpz_st);
- dns_db_detach(dbp);
- dns_zone_detach(zonep);
- policy = DNS_RPZ_POLICY_MISS;
+ trdataset = st->m.rdataset;
+ st->m.rdataset = *rdatasetp;
+ *rdatasetp = trdataset;
+ st->m.ttl = ISC_MIN(st->m.rdataset->ttl, rpz->max_policy_ttl);
+ } else {
+ st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, rpz->max_policy_ttl);
+ }
+ st->m.version = version;
+}
+
+/*
+ * Check this address in every eligible policy zone.
+ */
+static isc_result_t
+rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_rdataset_t **p_rdatasetp)
+{
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_st_t *st;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_prefix_t prefix;
+ dns_rpz_num_t rpz_num;
+ dns_fixedname_t ip_namef, p_namef;
+ dns_name_t *ip_name, *p_name;
+ dns_zone_t *p_zone;
+ dns_db_t *p_db;
+ dns_dbversion_t *p_version;
+ dns_dbnode_t *p_node;
+ dns_rpz_policy_t policy;
+ isc_result_t result;
+
+ dns_fixedname_init(&ip_namef);
+ ip_name = dns_fixedname_name(&ip_namef);
+
+ p_zone = NULL;
+ p_db = NULL;
+ p_node = NULL;
+
+ rpzs = client->view->rpzs;
+ st = client->query.rpz_st;
+ while (zbits != 0) {
+ rpz_num = dns_rpz_find_ip(rpzs, rpz_type, zbits, netaddr,
+ ip_name, &prefix);
+ if (rpz_num == DNS_RPZ_INVALID_NUM)
+ break;
+ zbits &= (DNS_RPZ_ZMASK(rpz_num) >> 1);
+
+ /*
+ * Do not try applying policy zones that cannot replace a
+ * previously found policy zone.
+ * Stop looking if the next best choice cannot
+ * replace what we already have.
+ */
+ rpz = rpzs->zones[rpz_num];
+ 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 ||
+ st->m.prefix > prefix))
+ break;
+ }
+
+ /*
+ * Get the policy for a prefix at least as long
+ * as the prefix of the entry we had before.
+ */
+ dns_fixedname_init(&p_namef);
+ p_name = dns_fixedname_name(&p_namef);
+ result = rpz_get_p_name(client, p_name, rpz, rpz_type, ip_name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = rpz_find_p(client, ip_name, qtype,
+ p_name, rpz, rpz_type,
+ &p_zone, &p_db, &p_version, &p_node,
+ p_rdatasetp, &policy);
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ /*
+ * Continue after a policy record that is missing
+ * contrary to the summary data. The summary
+ * data can out of date during races with and among
+ * policy zone updates.
+ */
+ continue;
+ case DNS_R_SERVFAIL:
+ rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ default:
+ /*
+ * Forget this policy if it is not preferable
+ * to the previously found policy.
+ * If this policy is not good, then stop looking
+ * because none of the later policy zones would work.
+ *
+ * With more than one applicable policy, prefer
+ * the earliest configured policy,
+ * QNAME over IP over NSDNAME over NSIP,
+ * the longest prefix
+ * the lexically smallest address.
+ * dns_rpz_find_ip() ensures st->m.rpz->num >= rpz->num.
+ * We can compare new and current p_name because
+ * both are of the same type and in the same zone.
+ * The tests above eliminate other reasons to
+ * reject this policy. If this policy can't work,
+ * then neither can later zones.
+ */
+ if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+ rpz->num == st->m.rpz->num &&
+ (st->m.type == rpz_type &&
+ st->m.prefix == prefix &&
+ 0 > dns_name_rdatacompare(st->p_name, p_name)))
+ break;
+
+ /*
+ * Stop checking after saving an enabled hit.
+ */
+ if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ rpz_save_p(st, rpz, rpz_type,
+ policy, p_name, prefix, result,
+ &p_zone, &p_db, &p_node,
+ p_rdatasetp, p_version);
+ break;
+ }
+
+ /*
+ * Log DNS_RPZ_POLICY_DISABLED zones
+ * and try the next eligible policy zone.
+ */
+ rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type,
+ p_zone, p_name);
+ }
+ }
+
+ rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Check the IP addresses in the A or AAAA rrsets for name against
+ * all eligible rpz_type (IP or NSIP) response policy rewrite rules.
+ */
+static isc_result_t
+rpz_rewrite_ip_rrset(ns_client_t *client,
+ dns_name_t *name, dns_rdatatype_t qtype,
+ dns_rpz_type_t rpz_type, dns_rdatatype_t ip_type,
+ dns_db_t **ip_dbp, dns_dbversion_t *ip_version,
+ dns_rdataset_t **ip_rdatasetp,
+ dns_rdataset_t **p_rdatasetp, isc_boolean_t resuming)
+{
+ dns_rpz_zbits_t zbits;
+ isc_netaddr_t netaddr;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ isc_result_t result;
+
+ zbits = rpz_get_zbits(client, ip_type, rpz_type);
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Get the A or AAAA rdataset.
+ */
+ result = rpz_rrset_find(client, name, ip_type, rpz_type, ip_dbp,
+ ip_version, ip_rdatasetp, resuming);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
break;
- default:
- dns_db_detach(dbp);
- dns_zone_detach(zonep);
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
- "", result);
- policy = DNS_RPZ_POLICY_ERROR;
- result = DNS_R_SERVFAIL;
+ case DNS_R_EMPTYNAME:
+ case DNS_R_EMPTYWILD:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NXRRSET:
+ case DNS_R_NCACHENXRRSET:
+ case ISC_R_NOTFOUND:
+ return (ISC_R_SUCCESS);
+ case DNS_R_DELEGATION:
+ case DNS_R_DUPLICATE:
+ case DNS_R_DROP:
+ return (result);
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type,
+ "NS address rewrite rrset ", result);
+ return (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_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, "NS address rewrite rrset ",
+ result);
+ }
+ return (DNS_R_SERVFAIL);
}
- *policyp = policy;
- return (result);
+ /*
+ * Check all of the IP addresses in the rdataset.
+ */
+ for (result = dns_rdataset_first(*ip_rdatasetp);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(*ip_rdatasetp)) {
+
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(*ip_rdatasetp, &rdata);
+ switch (rdata.type) {
+ case dns_rdatatype_a:
+ INSIST(rdata.length == 4);
+ memcpy(&ina.s_addr, rdata.data, 4);
+ isc_netaddr_fromin(&netaddr, &ina);
+ break;
+ case dns_rdatatype_aaaa:
+ INSIST(rdata.length == 16);
+ memcpy(in6a.s6_addr, rdata.data, 16);
+ isc_netaddr_fromin6(&netaddr, &in6a);
+ break;
+ default:
+ continue;
+ }
+
+ result = rpz_rewrite_ip(client, &netaddr, qtype, rpz_type,
+ zbits, p_rdatasetp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
}
/*
- * Build and look for a QNAME or NSDNAME owner name in a response policy zone.
+ * Look for IP addresses in A and AAAA rdatasets
+ * that trigger all eligible IP or NSIP policy rules.
*/
static isc_result_t
-rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
- dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp)
+rpz_rewrite_ip_rrsets(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rdataset_t **ip_rdatasetp, isc_boolean_t resuming)
{
dns_rpz_st_t *st;
+ dns_dbversion_t *ip_version;
+ dns_db_t *ip_db;
+ dns_rdataset_t *p_rdataset;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ ip_version = NULL;
+ ip_db = NULL;
+ p_rdataset = NULL;
+ if ((st->state & DNS_RPZ_DONE_IPv4) == 0 &&
+ (qtype == dns_rdatatype_a ||
+ qtype == dns_rdatatype_any ||
+ rpz_type == DNS_RPZ_TYPE_NSIP)) {
+ /*
+ * Rewrite based on an IPv4 address that will appear
+ * in the ANSWER section or if we are checking IP addresses.
+ */
+ result = rpz_rewrite_ip_rrset(client, name, qtype,
+ rpz_type, dns_rdatatype_a,
+ &ip_db, ip_version, ip_rdatasetp,
+ &p_rdataset, resuming);
+ if (result == ISC_R_SUCCESS)
+ st->state |= DNS_RPZ_DONE_IPv4;
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+ if (result == ISC_R_SUCCESS &&
+ (qtype == dns_rdatatype_aaaa ||
+ qtype == dns_rdatatype_any ||
+ rpz_type == DNS_RPZ_TYPE_NSIP)) {
+ /*
+ * Rewrite based on IPv6 addresses that will appear
+ * in the ANSWER section or if we are checking IP addresses.
+ */
+ result = rpz_rewrite_ip_rrset(client, name, qtype,
+ rpz_type, dns_rdatatype_aaaa,
+ &ip_db, ip_version, ip_rdatasetp,
+ &p_rdataset, resuming);
+ }
+ if (ip_db != NULL)
+ dns_db_detach(&ip_db);
+ query_putrdataset(client, &p_rdataset);
+ return (result);
+}
+
+/*
+ * Try to rewrite a request for a qtype rdataset based on the trigger name
+ * trig_name and rpz_type (DNS_RPZ_TYPE_QNAME or DNS_RPZ_TYPE_NSDNAME).
+ * Record the results including the replacement rdataset if any
+ * in client->query.rpz_st.
+ * *rdatasetp is a scratch rdataset.
+ */
+static isc_result_t
+rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t allowed_zbits, dns_rdataset_t **rdatasetp)
+{
dns_rpz_zone_t *rpz;
- dns_fixedname_t prefixf, rpz_qnamef;
- dns_name_t *prefix, *suffix, *rpz_qname;
- dns_zone_t *zone;
- dns_db_t *db;
- dns_dbversion_t *version;
- dns_dbnode_t *node;
+ dns_rpz_st_t *st;
+ dns_fixedname_t p_namef;
+ dns_name_t *p_name;
+ dns_rpz_zbits_t zbits;
+ dns_rpz_num_t rpz_num;
+ dns_zone_t *p_zone;
+ dns_db_t *p_db;
+ dns_dbversion_t *p_version;
+ dns_dbnode_t *p_node;
dns_rpz_policy_t policy;
- unsigned int labels;
isc_result_t result;
+ zbits = rpz_get_zbits(client, qtype, rpz_type);
+ zbits &= allowed_zbits;
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * If there is only one eligible policy zone, just check it.
+ * If more than one, then use the summary database to find
+ * the bit mask of policy zones with policies for this trigger name.
+ * x&-x is the least significant bit set in x
+ */
+ if (zbits != (zbits & -zbits)) {
+ zbits = dns_rpz_find_name(client->view->rpzs,
+ rpz_type, zbits, trig_name);
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_fixedname_init(&p_namef);
+ p_name = dns_fixedname_name(&p_namef);
+
+ p_zone = NULL;
+ p_db = NULL;
+ p_node = NULL;
+
st = client->query.rpz_st;
- zone = NULL;
- db = NULL;
- node = NULL;
- for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
- rpz != NULL;
- rpz = ISC_LIST_NEXT(rpz, link)) {
- if (!RECURSIONOK(client) && rpz->recursive_only)
+ /*
+ * Check the trigger name in every policy zone that the summary data
+ * says has a hit for the trigger name.
+ * Most of the time there are no eligible zones and the summary data
+ * keeps us from getting this far.
+ * We check the most eligible zone first and so usually check only
+ * one policy zone.
+ */
+ for (rpz_num = 0;
+ zbits != 0;
+ ++rpz_num, zbits >>= 1) {
+ if ((zbits & 1) == 0) {
+ INSIST(rpz_num <= client->view->rpzs->p.num_zones);
continue;
+ }
/*
- * Do not check policy zones that cannot replace a policy
- * already known to match.
+ * Do not check policy zones that cannot replace a previously
+ * found policy.
*/
+ rpz = client->view->rpzs->zones[rpz_num];
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);
- dns_name_split(qname, 1, prefix, NULL);
- if (rpz_type == DNS_RPZ_TYPE_NSDNAME)
- suffix = &rpz->nsdname;
- else
- suffix = &rpz->origin;
- dns_fixedname_init(&rpz_qnamef);
- rpz_qname = dns_fixedname_name(&rpz_qnamef);
- for (;;) {
- result = dns_name_concatenate(prefix, suffix,
- rpz_qname, NULL);
- if (result == ISC_R_SUCCESS)
break;
- INSIST(result == DNS_R_NAMETOOLONG);
- labels = dns_name_countlabels(prefix);
- if (labels < 2) {
- 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_log_fail(client, DNS_RPZ_DEBUG_LEVEL1,
- rpz_type, suffix,
- "concatentate() ", result);
- }
- dns_name_split(prefix, labels - 1, NULL, prefix);
}
/*
- * See if the policy record exists and get its policy.
+ * Get the next policy zone's record for this trigger name.
*/
- result = rpz_find(client, qtype, rpz_qname, qname, rpz,
- rpz_type, &zone, &db, &version, &node,
- rdatasetp, &policy);
+ result = rpz_get_p_name(client, p_name, rpz, rpz_type,
+ trig_name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = rpz_find_p(client, trig_name, qtype, p_name,
+ rpz, rpz_type,
+ &p_zone, &p_db, &p_version, &p_node,
+ rdatasetp, &policy);
switch (result) {
case DNS_R_NXDOMAIN:
- case DNS_R_EMPTYNAME:
- break;
+ /*
+ * Continue after a missing policy record
+ * contrary to the summary data. The summary
+ * data can out of date during races with and among
+ * policy zone updates.
+ */
+ continue;
case DNS_R_SERVFAIL:
- rpz_clean(&zone, &db, &node, rdatasetp);
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
st->m.policy = DNS_RPZ_POLICY_ERROR;
return (DNS_R_SERVFAIL);
default:
/*
- * 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
+ * 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))))
+ 0 >= dns_name_compare(p_name, st->p_name))))
continue;
-
+#if 0
/*
- * Merely log DNS_RPZ_POLICY_DISABLED hits.
+ * This code would block a customer reported information
+ * leak of rpz rules by rewriting requests in the
+ * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs.
+ * Without this code, a bad guy could request
+ * 24.0.3.2.10.rpz-ip. to find the policy rule for
+ * 10.2.3.0/14. It is an insignificant leak and this
+ * code is not worth its cost, because the bad guy
+ * could publish "evil.com A 10.2.3.4" and request
+ * evil.com to get the same information.
+ * Keep code with "#if 0" in case customer demand
+ * is irresistible.
+ *
+ * We have the less frequent case of a triggered
+ * policy. Check that we have not trigger on one
+ * of the pretend RPZ TLDs.
+ * This test would make it impossible to rewrite
+ * names in TLDs that start with "rpz-" should
+ * ICANN ever allow such TLDs.
*/
- if (rpz->policy == DNS_RPZ_POLICY_DISABLED) {
- rpz_log_rewrite(client, "disabled ",
- policy, rpz_type, rpz_qname);
- continue;
+ unsigned int labels;
+ labels = dns_name_countlabels(trig_name);
+ if (labels >= 2) {
+ dns_label_t label;
+
+ dns_name_getlabel(trig_name, labels-2, &label);
+ if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 &&
+ strncasecmp((const char *)label.base+1,
+ DNS_RPZ_PREFIX,
+ sizeof(DNS_RPZ_PREFIX)-1) == 0)
+ continue;
}
-
- rpz_match_clear(st);
- st->m.rpz = rpz;
- st->m.type = rpz_type;
- st->m.prefix = 0;
- st->m.policy = policy;
- st->m.result = result;
- dns_name_copy(rpz_qname, st->qname, NULL);
- if (*rdatasetp != NULL &&
- dns_rdataset_isassociated(*rdatasetp)) {
- dns_rdataset_t *trdataset;
-
- trdataset = st->m.rdataset;
- st->m.rdataset = *rdatasetp;
- *rdatasetp = trdataset;
- st->m.ttl = ISC_MIN(st->m.rdataset->ttl,
- rpz->max_policy_ttl);
- } else {
- st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT,
- rpz->max_policy_ttl);
+#endif
+ if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ rpz_save_p(st, rpz, rpz_type,
+ policy, p_name, 0, result,
+ &p_zone, &p_db, &p_node,
+ rdatasetp, p_version);
+ /*
+ * After a hit, higher numbered policy zones
+ * are irrelevant
+ */
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
+ return (ISC_R_SUCCESS);
}
- st->m.node = node;
- node = NULL;
- st->m.db = db;
- db = NULL;
- st->m.version = version;
- st->m.zone = zone;
- zone = NULL;
+ /*
+ * Log DNS_RPZ_POLICY_DISABLED zones
+ * and try the next eligible policy zone.
+ */
+ rpz_log_rewrite(client, ISC_TRUE, policy, rpz_type,
+ p_zone, p_name);
+ break;
}
}
- rpz_clean(&zone, &db, &node, rdatasetp);
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
return (ISC_R_SUCCESS);
}
st = client->query.rpz_st;
if (str != NULL)
- rpz_log_fail(client, level, DNS_RPZ_TYPE_NSIP, nsname,
+ rpz_log_fail(client, level, nsname, DNS_RPZ_TYPE_NSIP,
str, result);
if (st->r.ns_rdataset != NULL &&
dns_rdataset_isassociated(st->r.ns_rdataset))
dns_rdataset_t *rdataset;
dns_fixedname_t nsnamef;
dns_name_t *nsname;
- isc_boolean_t ck_ip;
+ int qresult_type;
+ dns_rpz_zbits_t zbits;
isc_result_t result;
st = client->query.rpz_st;
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->_p_namef);
dns_fixedname_init(&st->_r_namef);
dns_fixedname_init(&st->_fnamef);
- st->qname = dns_fixedname_name(&st->_qnamef);
+ st->p_name = dns_fixedname_name(&st->_p_namef);
st->r_name = dns_fixedname_name(&st->_r_namef);
st->fname = dns_fixedname_name(&st->_fnamef);
client->query.rpz_st = st;
case ISC_R_SUCCESS:
case DNS_R_GLUE:
case DNS_R_ZONECUT:
- ck_ip = ISC_TRUE;
+ qresult_type = 0;
break;
case DNS_R_EMPTYNAME:
case DNS_R_NXRRSET:
case DNS_R_NCACHENXRRSET:
case DNS_R_CNAME:
case DNS_R_DNAME:
- ck_ip = ISC_FALSE;
+ qresult_type = 1;
break;
case DNS_R_DELEGATION:
case ISC_R_NOTFOUND:
- return (ISC_R_SUCCESS);
+ qresult_type = 2;
+ break;
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);
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
+ DNS_RPZ_TYPE_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,
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
+ DNS_RPZ_TYPE_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 query name if this it the first time
- * for the current qname, i.e. we've not been recursing.
+ * Check triggers for the query name if this is the first time
+ * for the current qname.
* There is a first time for each name in a CNAME chain.
*/
- result = rpz_rewrite_name(client, qtype, client->query.qname,
- DNS_RPZ_TYPE_QNAME, &rdataset);
+ if (qresult_type == 2) {
+ /*
+ * This request needs recursion that has not been done.
+ * Get bits for the qname policies that do not need
+ * recursion the results of recursion.
+ * Bail out to wait for recursion if there are no
+ * such policies.
+ */
+ zbits = client->view->rpzs->have.qname_skip_recurse;
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+ } else {
+ zbits = DNS_RPZ_ALL_ZBITS;
+ }
+ result = rpz_rewrite_name(client, client->query.qname, qtype,
+ DNS_RPZ_TYPE_QNAME, zbits, &rdataset);
if (result != ISC_R_SUCCESS)
goto cleanup;
+ /*
+ * This may have been an attempt to find a qname trigger
+ * before required recursion (qresult_type == 2).
+ * If so and and no pre-recursion triggers hit, quit and
+ * do everything again after recursing. Do not bother saving
+ * the work already done, because recursion is so slow.
+ * If a pre-recursion trigger hit, then no other triggers
+ * can be relevant and so quit.
+ */
+ if (qresult_type == 2)
+ goto cleanup;
+
st->r.label = dns_name_countlabels(client->query.qname);
+ /*
+ * Check IP addresses for the qname next,
+ * starting with IPv4 addresses.
+ */
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.
+ * Check known IP addresses for the query name if the database
+ * lookup resulted in some addresses (qresult_type == 0)
+ * and if we have not already checked them.
* 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 ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 && qresult_type == 0 &&
+ rpz_get_zbits(client, qtype, DNS_RPZ_TYPE_IP) != 0) {
+ result = rpz_rewrite_ip_rrsets(client,
+ client->query.qname,
+ qtype, DNS_RPZ_TYPE_IP,
+ &rdataset, resuming);
if (result != ISC_R_SUCCESS)
goto cleanup;
- st->state &= ~DNS_RPZ_DONE_IPv4;
+ /*
+ * We are finished checking the IP addresses for the qname.
+ * Start with IPv4 if we will check NS IP addesses.
+ */
st->state |= DNS_RPZ_DONE_QNAME_IP;
+ st->state &= ~DNS_RPZ_DONE_IPv4;
}
/*
- * Stop looking for rules if there are none of the other kinds.
+ * Stop looking for rules if there are none of the other kinds
+ * that could override what we already have.
*/
- if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 |
- DNS_RPZ_HAVE_NSDNAME)) == 0) {
+ if (rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSDNAME) == 0 &&
+ rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSIP) == 0) {
result = ISC_R_SUCCESS;
goto cleanup;
}
dns_fixedname_init(&nsnamef);
dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
- while (st->r.label > 1) {
+ while (st->r.label > client->view->rpzs->p.min_ns_labels) {
/*
* Get NS rrset for each domain in the current qname.
*/
if (st->r.ns_rdataset == NULL ||
!dns_rdataset_isassociated(st->r.ns_rdataset)) {
dns_db_t *db = NULL;
- result = rpz_rrset_find(client, DNS_RPZ_TYPE_NSDNAME,
- nsname, dns_rdatatype_ns,
+ result = rpz_rrset_find(client, nsname, dns_rdatatype_ns,
+ DNS_RPZ_TYPE_NSDNAME,
&db, NULL, &st->r.ns_rdataset,
resuming);
if (db != NULL)
dns_rdata_reset(&nsrdata);
if (result != ISC_R_SUCCESS) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
- DNS_RPZ_TYPE_NSIP, nsname,
+ nsname, DNS_RPZ_TYPE_NSIP,
"rdata_tostruct() ", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
goto cleanup;
* 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,
+ if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0) {
+ result = rpz_rewrite_name(client, &ns.name,
+ qtype,
DNS_RPZ_TYPE_NSDNAME,
+ DNS_RPZ_ALL_ZBITS,
&rdataset);
if (result != ISC_R_SUCCESS) {
dns_rdata_freestruct(&ns);
/*
* Check all IP addresses for this NS name.
*/
- result = rpz_rewrite_rrsets(client, DNS_RPZ_TYPE_NSIP,
- &ns.name, dns_rdatatype_any,
- &rdataset, resuming);
+ result = rpz_rewrite_ip_rrsets(client, &ns.name, qtype,
+ DNS_RPZ_TYPE_NSIP,
+ &rdataset, resuming);
dns_rdata_freestruct(&ns);
if (result != ISC_R_SUCCESS)
goto cleanup;
} while (result == ISC_R_SUCCESS);
dns_rdataset_disassociate(st->r.ns_rdataset);
st->r.label--;
+
+ if (rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSDNAME) == 0 &&
+ rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSIP) == 0)
+ break;
}
/*
- * Use the best, if any, hit.
+ * Use the best hit, if any.
*/
result = ISC_R_SUCCESS;
st->m.policy == DNS_RPZ_POLICY_ERROR) {
if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU &&
result != DNS_R_DELEGATION)
- rpz_log_rewrite(client, "", st->m.policy, st->m.type,
- st->qname);
+ rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+ st->m.type, st->m.zone, st->p_name);
rpz_match_clear(st);
}
if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
}
/*
- * See if response policy zone rewriting is allowed a lack of interest
+ * See if response policy zone rewriting is allowed by a lack of interest
* by the client in DNSSEC or a lack of signatures.
*/
static isc_boolean_t
-rpz_ck_dnssec(ns_client_t *client, isc_result_t result,
+rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
{
dns_fixedname_t fixed;
dns_name_t *found;
dns_rdataset_t trdataset;
dns_rdatatype_t type;
+ isc_result_t result;
- if (client->view->rpz_break_dnssec)
+ if (client->view->rpzs->p.break_dnssec || !WANTDNSSEC(client))
return (ISC_TRUE);
+
/*
- * sigrdataset == NULL if and only !WANTDNSSEC(client)
+ * We do not know if there are signatures if we have not recursed
+ * for them.
*/
+ if (qresult == DNS_R_DELEGATION || qresult == ISC_R_NOTFOUND)
+ return (ISC_FALSE);
+
if (sigrdataset == NULL)
return (ISC_TRUE);
if (dns_rdataset_isassociated(sigrdataset))
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);
+ rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
+ st->m.type, st->m.zone, st->p_name);
ns_client_qnamereplace(client, fname);
/*
* Turn off DNSSEC because the results of a
}
}
- if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
- (RECURSIONOK(client) || !client->view->rpz_recursive_only) &&
+ if (client->view->rpzs != NULL &&
+ client->view->rpzs->p.num_zones != 0 &&
+ (RECURSIONOK(client) || client->view->rpzs->p.no_rd_ok != 0) &&
rpz_ck_dnssec(client, result, rdataset, sigrdataset) &&
!RECURSING(client) &&
(client->query.rpz_st == NULL ||
(client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) &&
- !dns_name_equal(client->query.qname, dns_rootname)) {
+ !dns_name_equal(client->query.qname, dns_rootname))
+ {
isc_result_t rresult;
rresult = rpz_rewrite(client, qtype, result, resuming);
query_putrdataset(client, &sigrdataset);
rpz_st->q.is_zone = is_zone;
is_zone = ISC_TRUE;
- rpz_log_rewrite(client, "", rpz_st->m.policy,
- rpz_st->m.type, rpz_st->qname);
+ rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy,
+ rpz_st->m.type, zone, rpz_st->p_name);
}
}
static isc_result_t
configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
- cfg_aclconfctx_t *aclconf, isc_boolean_t added);
+ cfg_aclconfctx_t *aclconf, isc_boolean_t added,
+ isc_boolean_t old_rpz_ok);
static isc_result_t
add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
}
static isc_result_t
-configure_rpz(dns_view_t *view, const cfg_listelt_t *element,
- isc_boolean_t recursive_only_def, dns_ttl_t ttl_def)
+configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const char *msg)
{
- const cfg_obj_t *rpz_obj, *policy_obj, *obj;
+ isc_result_t result;
+
+ result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid %s '%s'", msg, str);
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const dns_name_t *origin)
+{
+ isc_result_t result;
+
+ result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE,
+ view->mctx);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid zone '%s'", str);
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
+ isc_boolean_t recursive_only_def, dns_ttl_t ttl_def,
+ const dns_rpz_zone_t *old, isc_boolean_t *old_rpz_okp)
+{
+ const cfg_obj_t *rpz_obj, *obj;
const char *str;
- dns_rpz_zone_t *old, *new;
- dns_zone_t *zone = NULL;
+ dns_rpz_zone_t *new;
isc_result_t result;
+ dns_rpz_num_t rpz_num;
+
+ REQUIRE(old != NULL || !*old_rpz_okp);
+
+ rpz_obj = cfg_listelt_value(element);
+
+ if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES)
+ return (ISC_R_NOMEMORY);
- new = isc_mem_get(view->mctx, sizeof(*new));
+ new = isc_mem_get(view->rpzs->mctx, sizeof(*new));
if (new == NULL) {
- result = ISC_R_NOMEMORY;
- goto cleanup;
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "no memory for response policy zones");
+ return (ISC_R_NOMEMORY);
}
memset(new, 0, sizeof(*new));
+ result = isc_refcount_init(&new->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(view->rpzs->mctx, new, sizeof(*new));
+ return (result);
+ }
dns_name_init(&new->origin, NULL);
+ dns_name_init(&new->ip, NULL);
dns_name_init(&new->nsdname, NULL);
- dns_name_init(&new->cname, NULL);
+ dns_name_init(&new->nsip, NULL);
dns_name_init(&new->passthru, NULL);
- ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
-
- rpz_obj = cfg_listelt_value(element);
- policy_obj = cfg_tuple_get(rpz_obj, "policy");
- if (cfg_obj_isvoid(policy_obj)) {
- new->policy = DNS_RPZ_POLICY_GIVEN;
- } else {
- str = cfg_obj_asstring(cfg_tuple_get(policy_obj,
- "policy name"));
- new->policy = dns_rpz_str2policy(str);
- INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
- }
+ dns_name_init(&new->cname, NULL);
+ new->num = view->rpzs->p.num_zones++;
+ view->rpzs->zones[new->num] = new;
obj = cfg_tuple_get(rpz_obj, "recursive-only");
- if (cfg_obj_isvoid(obj)) {
- new->recursive_only = recursive_only_def;
+ if (cfg_obj_isvoid(obj) ? recursive_only_def : cfg_obj_asboolean(obj)) {
+ view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(new->num);
} else {
- new->recursive_only = cfg_obj_asboolean(obj);
+ view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(new->num);
}
- if (!new->recursive_only)
- view->rpz_recursive_only = ISC_FALSE;
obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
if (cfg_obj_isuint32(obj)) {
} else {
new->max_policy_ttl = ttl_def;
}
+ if (*old_rpz_okp && new->max_policy_ttl != old->max_policy_ttl)
+ *old_rpz_okp = ISC_FALSE;
str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
- result = dns_name_fromstring(&new->origin, str, DNS_NAME_DOWNCASE,
- view->mctx);
- if (result != ISC_R_SUCCESS) {
+ result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (dns_name_equal(&new->origin, dns_rootname)) {
cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "invalid zone '%s'", str);
- goto cleanup;
+ "invalid zone name '%s'", str);
+ return (DNS_R_EMPTYLABEL);
}
-
- result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE,
- &new->origin, DNS_NAME_DOWNCASE,
- view->mctx);
- if (result != ISC_R_SUCCESS) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "invalid zone '%s'", str);
- goto cleanup;
+ for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) {
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
+ &new->origin)) {
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "duplicate '%s'", str);
+ result = DNS_R_DUPLICATE;
+ return (result);
+ }
}
+ if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin))
+ *old_rpz_okp = ISC_FALSE;
- result = dns_name_fromstring(&new->passthru, DNS_RPZ_PASSTHRU_ZONE,
- DNS_NAME_DOWNCASE, view->mctx);
- if (result != ISC_R_SUCCESS) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "invalid zone '%s'", str);
- goto cleanup;
- }
+ result = configure_rpz_name2(view, rpz_obj, &new->ip,
+ DNS_RPZ_IP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
- result = dns_view_findzone(view, &new->origin, &zone);
- if (result != ISC_R_SUCCESS) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "unknown zone '%s'", str);
- goto cleanup;
- }
- if (dns_zone_gettype(zone) != dns_zone_master &&
- dns_zone_gettype(zone) != dns_zone_slave) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "zone '%s' is neither master nor slave", str);
- dns_zone_detach(&zone);
- result = DNS_R_NOTMASTER;
- goto cleanup;
- }
- dns_zone_detach(&zone);
+ result = configure_rpz_name2(view, rpz_obj, &new->nsdname,
+ DNS_RPZ_NSDNAME_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
- for (old = ISC_LIST_HEAD(view->rpz_zones);
- old != new;
- old = ISC_LIST_NEXT(old, link)) {
- ++new->num;
- if (dns_name_equal(&old->origin, &new->origin)) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "duplicate '%s'", str);
- result = DNS_R_DUPLICATE;
- goto cleanup;
+ result = configure_rpz_name2(view, rpz_obj, &new->nsip,
+ DNS_RPZ_NSIP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->passthru,
+ DNS_RPZ_PASSTHRU_ZONE, "zone");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(rpz_obj, "policy");
+ if (cfg_obj_isvoid(obj)) {
+ new->policy = DNS_RPZ_POLICY_GIVEN;
+ } else {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+ new->policy = dns_rpz_str2policy(str);
+ INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
+ if (new->policy == DNS_RPZ_POLICY_CNAME) {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
+ result = configure_rpz_name(view, rpz_obj, &new->cname,
+ str, "cname");
+ if (result != ISC_R_SUCCESS)
+ return (result);
}
}
+ if (*old_rpz_okp && (new->policy != old->policy ||
+ !dns_name_equal(&old->cname, &new->cname)))
+ *old_rpz_okp = ISC_FALSE;
+
+ return (ISC_R_SUCCESS);
+}
- if (new->policy == DNS_RPZ_POLICY_CNAME) {
- str = cfg_obj_asstring(cfg_tuple_get(policy_obj, "cname"));
- result = dns_name_fromstring(&new->cname, str,
- DNS_NAME_DOWNCASE, view->mctx);
+static isc_result_t
+configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
+ isc_boolean_t *old_rpz_okp)
+{
+ const cfg_listelt_t *zone_element;
+ const cfg_obj_t *sub_obj;
+ isc_boolean_t recursive_only_def;
+ dns_ttl_t ttl_def;
+ dns_rpz_zones_t *new;
+ const dns_rpz_zones_t *old;
+ dns_view_t *pview;
+ const dns_rpz_zone_t *old_zone;
+ isc_result_t result;
+ int i;
+
+ *old_rpz_okp = ISC_FALSE;
+
+ zone_element = cfg_list_first(cfg_tuple_get(rpz_obj, "zone list"));
+ if (zone_element == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = dns_rpz_new_zones(&view->rpzs, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ new = view->rpzs;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
+ if (!cfg_obj_isvoid(sub_obj) &&
+ !cfg_obj_asboolean(sub_obj))
+ recursive_only_def = ISC_FALSE;
+ else
+ recursive_only_def = ISC_TRUE;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec");
+ if (!cfg_obj_isvoid(sub_obj) &&
+ cfg_obj_asboolean(sub_obj))
+ new->p.break_dnssec = ISC_TRUE;
+ else
+ new->p.break_dnssec = ISC_FALSE;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+ if (cfg_obj_isuint32(sub_obj))
+ ttl_def = cfg_obj_asuint32(sub_obj);
+ else
+ ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots");
+ if (cfg_obj_isuint32(sub_obj))
+ new->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1;
+ else
+ new->p.min_ns_labels = 2;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj))
+ new->p.qname_wait_recurse = ISC_TRUE;
+ else
+ new->p.qname_wait_recurse = ISC_FALSE;
+
+ pview = NULL;
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS) {
+ old = pview->rpzs;
+ } else {
+ old = NULL;
+ }
+ if (old == NULL)
+ *old_rpz_okp = ISC_FALSE;
+ else
+ *old_rpz_okp = ISC_TRUE;
+
+ for (i = 0;
+ zone_element != NULL;
+ ++i, zone_element = cfg_list_next(zone_element)) {
+ if (*old_rpz_okp && i < old->p.num_zones) {
+ old_zone = old->zones[i];
+ } else {
+ *old_rpz_okp = ISC_FALSE;
+ old_zone = NULL;
+ }
+ result = configure_rpz_zone(view, zone_element,
+ recursive_only_def, ttl_def,
+ old_zone, old_rpz_okp);
if (result != ISC_R_SUCCESS) {
- cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "invalid cname '%s'", str);
- goto cleanup;
+ if (pview != NULL)
+ dns_view_detach(&pview);
+ return (result);
}
}
- return (ISC_R_SUCCESS);
+ /*
+ * If this is a reloading and the parameters and list of policy
+ * zones are unchanged, then use the same policy data.
+ * Data for individual zones that must be reloaded will be merged.
+ */
+ if (old != NULL && memcmp(&old->p, &new->p, sizeof(new->p)) != 0)
+ *old_rpz_okp = ISC_FALSE;
+ if (*old_rpz_okp) {
+ dns_rpz_detach_rpzs(&view->rpzs);
+ dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
+ }
+ if (pview != NULL)
+ dns_view_detach(&pview);
- cleanup:
- dns_rpz_view_destroy(view);
- return (result);
+ return (ISC_R_SUCCESS);
}
#define CHECK_RRL(obj, cond, pat, val1, val2) \
dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
unsigned int query_timeout, ndisp;
struct cfg_context *nzctx;
+ isc_boolean_t old_rpz_ok = ISC_FALSE;
REQUIRE(DNS_VIEW_VALID(view));
&view->queryacl));
}
+ /*
+ * Make the list of response policy zone names for a view that
+ * is used for real lookups and so cares about hints.
+ */
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in && need_hints &&
+ ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
+ CHECK(configure_rpz(view, obj, &old_rpz_ok));
+ }
+
/*
* Configure the zones.
*/
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
- actx, ISC_FALSE));
+ actx, ISC_FALSE, old_rpz_ok));
+ }
+
+ /*
+ * Check that a master or slave zone was found for each
+ * zone named in the response policy statement.
+ */
+ if (view->rpzs != NULL) {
+ dns_rpz_num_t n;
+
+ for (n = 0; n < view->rpzs->p.num_zones; ++n)
+ {
+ if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&view->rpzs->zones[n]->origin,
+ namebuf, sizeof(namebuf));
+ cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "'%s' is not a master or slave zone",
+ namebuf);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+ }
}
/*
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig,
mctx, view, actx,
- ISC_TRUE));
+ ISC_TRUE, ISC_FALSE));
}
}
}
}
- /*
- * Make the list of response policy zone names for views that
- * are used for real lookups and so care about hints.
- */
- obj = NULL;
- if (view->rdclass == dns_rdataclass_in && need_hints &&
- ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
- const cfg_obj_t *recursive_only_obj;
- const cfg_obj_t *break_dnssec_obj, *ttl_obj;
- isc_boolean_t recursive_only_def;
- dns_ttl_t ttl_def;
-
- recursive_only_obj = cfg_tuple_get(obj, "recursive-only");
- if (!cfg_obj_isvoid(recursive_only_obj) &&
- !cfg_obj_asboolean(recursive_only_obj))
- recursive_only_def = ISC_FALSE;
- else
- recursive_only_def = ISC_TRUE;
-
- break_dnssec_obj = cfg_tuple_get(obj, "break-dnssec");
- if (!cfg_obj_isvoid(break_dnssec_obj) &&
- cfg_obj_asboolean(break_dnssec_obj))
- view->rpz_break_dnssec = ISC_TRUE;
- else
- view->rpz_break_dnssec = ISC_FALSE;
-
- ttl_obj = cfg_tuple_get(obj, "max-policy-ttl");
- if (cfg_obj_isuint32(ttl_obj))
- ttl_def = cfg_obj_asuint32(ttl_obj);
- else
- ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
-
- for (element = cfg_list_first(cfg_tuple_get(obj, "zone list"));
- element != NULL;
- element = cfg_list_next(element)) {
- result = configure_rpz(view, element,
- recursive_only_def, ttl_def);
- if (result != ISC_R_SUCCESS)
- goto cleanup;
- dns_rpz_set_need(ISC_TRUE);
- }
- }
-
obj = NULL;
result = ns_config_get(maps, "rate-limit", &obj);
if (result == ISC_R_SUCCESS) {
static isc_result_t
configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
- cfg_aclconfctx_t *aclconf, isc_boolean_t added)
+ cfg_aclconfctx_t *aclconf, isc_boolean_t added,
+ isc_boolean_t old_rpz_ok)
{
dns_view_t *pview = NULL; /* Production view */
dns_zone_t *zone = NULL; /* New or reused zone */
const char *zname;
dns_rdataclass_t zclass;
const char *ztypestr;
+ dns_rpz_num_t rpz_num;
options = NULL;
(void)cfg_map_get(config, "options", &options);
}
INSIST(dupzone == NULL);
+ /*
+ * Note whether this is a response policy zone and which one if so.
+ */
+ for (rpz_num = 0; ; ++rpz_num) {
+ if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones) {
+ rpz_num = DNS_RPZ_INVALID_NUM;
+ break;
+ }
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, origin))
+ break;
+ }
+
/*
* See if we can reuse an existing zone. This is
* only possible if all of these are true:
* - The zone is compatible with the config
* options (e.g., an existing master zone cannot
* be reused if the options specify a slave zone)
+ * - The zone was not and is still not a response policy zone
+ * or the zone is a policy zone with an unchanged number
+ * and we are using the old policy zone summary data.
*/
result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
view->rdclass, &pview);
if (zone != NULL && !ns_zone_reusable(zone, zconfig))
dns_zone_detach(&zone);
+ if (zone != NULL && (rpz_num != dns_zone_get_rpz_num(zone) ||
+ (rpz_num != DNS_RPZ_INVALID_NUM && !old_rpz_ok)))
+ dns_zone_detach(&zone);
+
if (zone != NULL) {
/*
* We found a reusable zone. Make it use the
dns_zone_setstats(zone, ns_g_server->zonestats);
}
+ if (rpz_num != DNS_RPZ_INVALID_NUM) {
+ result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': incompatible"
+ " masterfile-format or database"
+ " for a response policy zone",
+ zname);
+ goto cleanup;
+ }
+ }
+
/*
* If the zone contains a 'forwarders' statement, configure
* selective forwarding.
isc_task_beginexclusive(server->task);
dns_view_thaw(view);
result = configure_zone(cfg->config, parms, vconfig,
- server->mctx, view, cfg->actx, ISC_FALSE);
+ server->mctx, view, cfg->actx, ISC_FALSE,
+ ISC_FALSE);
dns_view_freeze(view);
isc_task_endexclusive(server->task);
if (result != ISC_R_SUCCESS)
"RateDropped");
SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
"RateSlipped");
+ SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
+ "RPZRewrites");
INSIST(i == dns_nsstatscounter_max);
/* Initialize resolver statistics */
-# Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: Makefile.in,v 1.3 2011/01/13 04:59:24 tbox Exp $
srcdir = @srcdir@
VPATH = @srcdir@
-# Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: clean.sh,v 1.6 2012/01/07 23:46:53 tbox Exp $
# Clean up after rpz tests.
-rm -f proto.* dsset-* random.data trusted.conf dig.out* nsupdate.tmp ns*/*tmp
+rm -f proto.* dsset-* random.data trusted.conf dig.out* nsupdate.tmp ns*/*tmp
rm -f ns*/*.key ns*/*.private ns2/tld2s.db
rm -f ns3/bl*.db ns*/*switch ns5/requests ns5/example.db ns5/bl.db ns5/*.perf
-rm -f */named.memstats */named.run */named.rpz */session.key
+rm -f */named.memstats */named.run */named.stats */session.key
rm -f */*.jnl */*.core */*.pid
/*
- * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.3 2011/01/13 04:59:24 tbox Exp $ */
controls { /* empty */ };
-; Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: root.db,v 1.6 2012/01/07 23:46:53 tbox Exp $
$TTL 120
-@ SOA ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
-@ NS ns.
+. 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.
; rewrite responses from this zone
tld4. NS ns.tld4.
ns.tld4. A 10.53.0.4
+
+; performance test
+tld5. NS ns.tld5.
+ns.tld5. A 10.53.0.5
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: base-tld2s.db,v 1.1.2.1 2012/02/24 17:22:37 vjs Exp $
; RPZ rewrite responses from this signed zone
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $
-
-. 0 NS ns1.
-ns1. 0 A 10.53.0.1
+. 120 NS ns.
+ns. 120 A 10.53.0.1
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.6 2012/01/07 23:46:53 tbox Exp $ */
controls { /* empty */ };
-; Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: tld2.db,v 1.4.4.2 2012/02/24 17:22:37 vjs Exp $
; RPZ rewrite responses from this zone
A 192.168.5.2
TXT "a5-1-2 tld2 text"
+a5-2 A 192.168.5.2
+ TXT "a5-2 tld2 text"
+
a5-3 A 192.168.5.3
TXT "a5-3 tld2 text"
-; Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: base.db,v 1.6.4.1 2011/10/15 23:03:38 vjs Exp $
; RPZ test
redirect A 127.0.0.1
*.redirect A 127.0.0.1
*.credirect CNAME google.com.
+
+
+; names in the RPZ TLDs that some say should not be rewritten.
+; This is not a bug, because any data leaked by writing 24.4.3.2.10.rpz-ip
+; (or whatever) is available by publishing "foo A 10.2.3.4" and then
+; resolving foo.
+32.3.2.1.127.rpz-ip CNAME walled.invalid.
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: crash1,v 1.2 2011/10/13 04:53:06 marka Exp $
; a bad zone that caused a crash related to dns_rdataset_disassociate()
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: crash2,v 1.3 2011/11/18 19:32:13 each Exp $
; a valid zone containing records that caused crashes
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: hints,v 1.4 2011/10/13 01:32:33 vjs Exp $
-
-. 0 NS ns1.
-ns1. 0 A 10.53.0.1
+. 120 NS ns.
+ns. 120 A 10.53.0.1
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.5 2011/10/28 11:46:50 marka Exp $ */
/*
transfer-source 10.53.0.3;
port 5300;
pid-file "named.pid";
+ statistics-file "named.stats";
session-keyfile "session.key";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
zone "bl-cname" policy cname txt-only.tld2.;
zone "bl-wildcname" policy cname *.tld4.;
zone "bl-garden" policy cname a12.tld2.;
- };
+ } min-ns-dots 0
+ # qname-wait-recurse no
+ ;
};
key rndc_key {
inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
};
-logging {
- # 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; };
-};
-
-// include "../trusted.conf";
zone "." { type hint; file "hints"; };
zone "bl." {type master; file "bl.db";
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: hints,v 1.2 2011/10/13 04:53:06 marka Exp $
-. 0 NS ns1.
-ns1. 0 A 10.53.0.1
+. 120 NS ns.
+ns. 120 A 10.53.0.1
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $ */
controls { /* empty */ };
-; Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: tld4.db,v 1.2.6.1 2012/02/24 17:22:37 vjs Exp $
; RPZ rewrite responses from this zone
-; Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: hints,v 1.1.2.1 2011/10/15 23:00:04 vjs Exp $
-
-. 0 NS ns1.
-ns1. 0 A 10.53.0.1
+. 120 NS ns.
+ns. 120 A 10.53.0.1
--- /dev/null
+# run the performace test close to real life
+
+-c named.conf -g
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.1.2.2 2012/02/24 17:22:38 vjs Exp $ */
/*
transfer-source 10.53.0.5;
port 5300;
pid-file "named.pid";
+ statistics-file "named.stats";
session-keyfile "session.key";
listen-on { 10.53.0.5; };
listen-on-v6 { none; };
notify no;
- # Eventually turn rpz on.
+ # turn rpz on or off
include "rpz-switch";
};
secret "1234abcd8765";
algorithm hmac-md5;
};
-controls { inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; }; };
+controls {
+ inet 10.53.0.5 port 9953 allow { any; } keys { rndc_key; };
+};
include "../trusted.conf";
zone "." {type hint; file "hints"; };
-zone "example.com." {type master; file "example.db"; };
+zone "tld5." {type master; file "tld5.db"; };
+zone "example.tld5." {type master; file "example.db"; };
-zone "bl." {type master; file "bl.db"; };
+zone "bl0." {type master; file "bl.db"; };
+zone "bl1." {type master; file "bl.db"; };
+zone "bl2." {type master; file "bl.db"; };
+zone "bl3." {type master; file "bl.db"; };
+zone "bl4." {type master; file "bl.db"; };
+zone "bl5." {type master; file "bl.db"; };
+zone "bl6." {type master; file "bl.db"; };
+zone "bl7." {type master; file "bl.db"; };
+zone "bl8." {type master; file "bl.db"; };
+zone "bl9." {type master; file "bl.db"; };
+zone "bl10." {type master; file "bl.db"; };
+zone "bl11." {type master; file "bl.db"; };
+zone "bl12." {type master; file "bl.db"; };
+zone "bl13." {type master; file "bl.db"; };
+zone "bl14." {type master; file "bl.db"; };
+zone "bl15." {type master; file "bl.db"; };
+zone "bl16." {type master; file "bl.db"; };
+zone "bl17." {type master; file "bl.db"; };
+zone "bl18." {type master; file "bl.db"; };
+zone "bl19." {type master; file "bl.db"; };
--- /dev/null
+; Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+
+
+; RPZ preformance test
+
+$TTL 120
+@ SOA . hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
+ NS ns
+ NS ns1
+ NS ns2
+ NS ns3
+ns A 10.53.0.5
+ns1 A 10.53.0.5
+ns2 A 10.53.0.5
+ns3 A 10.53.0.5
+
+
+$ORIGIN example.tld5.
+example.tld5. NS ns
+ NS ns1
+ns A 10.53.0.5
+ns1 A 10.53.0.5
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: qperf.sh,v 1.1.2.1 2011/10/15 23:03:37 vjs Exp $
for QDIR in `echo "$PATH" | tr : ' '` ../../../../contrib/queryperf; do
QPERF=$QDIR/queryperf
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rpz.c,v 1.3.226.1 2011/10/15 23:03:37 vjs Exp $ */
#include <config.h>
#! /bin/sh
#
-# Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: setup.sh,v 1.6 2012/01/07 23:46:53 tbox Exp $
set -e
# Performance checks.
-# First with rpz off.
cat <<EOF >ns5/rpz-switch
-response-policy {zone "bl";}
- recursive-only no
- max-policy-ttl 90
- break-dnssec yes;
+response-policy {
+ zone "bl0"; zone "bl1"; zone "bl2"; zone "bl3"; zone "bl4";
+ zone "bl5"; zone "bl6"; zone "bl7"; zone "bl8"; zone "bl9";
+ zone "bl10"; zone "bl11"; zone "bl12"; zone "bl13"; zone "bl14";
+ zone "bl15"; zone "bl16"; zone "bl17"; zone "bl18"; zone "bl19";
+ } recursive-only no
+ max-policy-ttl 90
+ # min-ns-dots 0
+ break-dnssec yes;
EOF
cat <<EOF >ns5/example.db
\$TTL 120
-@ SOA . hostmaster.ns.example. ( 1 3600 1200 604800 60 )
+@ SOA . hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
NS ns
+ NS ns1
ns A 10.53.0.5
+ns1 A 10.53.0.5
EOF
cat <<EOF >ns5/bl.db
; used only in failure for "recursive-only no" in #8 test5
a3-5.tld2 CNAME *.
-; for "break-dnssec" in #9 test5
+; for "break-dnssec" in #9 & #10 test5
a3-5.tld2s CNAME *.
-; for "max-policy-ttl 90" in test5
+; for "max-policy-ttl 90" in #17 test5
a3-17.tld2 500 A 17.17.17.17
-; dummy NSDNAME policies to trigger lookups
-ns-1.example.com.rpz-nsdname CNAME .
-ns-2.example.com.rpz-nsdname CNAME .
-ns-3.example.com.rpz-nsdname CNAME .
-ns-4.example.com.rpz-nsdname CNAME .
-ns-5.example.com.rpz-nsdname CNAME .
+; dummy NSDNAME policy to trigger lookups
+ns1.x.rpz-nsdname CNAME .
EOF
if test -n "$QPERF"; then
# do not build the full zones if we will not use them to avoid the long
# time otherwise required to shut down the server
$PERL -e 'for ($val = 1; $val <= 65535; ++$val) {
- printf("host-%d-%d\tA 192.168.%d.%d\n",
- $val/256, $val%256, $val/256, $val%256);
+ printf("host-%05d\tA 192.168.%d.%d\n", $val, $val/256, $val%256);
}' >>ns5/example.db
echo >>ns5/bl.db
echo "; rewrite some names" >>ns5/bl.db
$PERL -e 'for ($val = 2; $val <= 65535; $val += 69) {
- printf("host-%d.sub%d.example.com\tCNAME\t.\n", $val/256, $val%256);
+ printf("host-%05d.example.tld5\tCNAME\t.\n", $val);
}' >>ns5/bl.db
echo >>ns5/bl.db
$PERL -e 'for ($val = 3; $val <= 65535; $val += 69) {
printf("32.%d.%d.168.192.rpz-ip \tCNAME\t.\n",
$val%256, $val/256);
- printf("32.%d.%d.168.192.rpz-nsip\tCNAME\t.\n",
- ($val+1)%256, ($val+1)/256);
}' >>ns5/bl.db
fi
# some psuedo-random queryperf requests
-$PERL -e 'for ($cnt = $val = 1; $cnt <= 2000; ++$cnt) {
- printf("host-%d.sub%d.example.com A\n", $val%256, $val/256);
- $val = ($val * 9 + 32771) % 65536;
+$PERL -e 'for ($cnt = $val = 1; $cnt <= 3000; ++$cnt) {
+ printf("host-%05d.example.tld5 A\n", $val);
+ $val = ($val * 9 + 32771) % 65536;
}' >ns5/requests
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: test1,v 1.8 2012/01/07 00:19:59 each Exp $
; Use comment lines instead of blank lines to combine update requests into
; QNAME tests
; NXDOMAIN
-; 2, 20, 25
+; 2, 25
update add a0-1.tld2.bl. 300 CNAME .
; NODATA
-; 3, 21
+; 3
update add a3-1.tld2.bl. 300 CNAME *.
; and no assert-botch
-; 5, 22
+; 4, 5
update add a3-2.tld2.bl. 300 DNAME example.com.
;
; NXDOMAIN for a4-2-cname.tld2 via its target a4-2.tld2.
; 19
update add a4-6.tld2.bl. 300 CNAME .
update add a4-6-cname.tld2.bl. 300 A 127.0.0.17
+; no change instead of NXDOMAIN because +norecurse
+; 20
+update add a5-2.tld2.bl. 300 CNAME .
+; no change instead of NODATA because +norecurse
+; 21
+update add a5-3.tld2.bl. 300 CNAME *.
+; 22, 23
+update add a5-4.tld2.bl. 300 DNAME example.com.
;
; assert in rbtdb.c
; 24
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: test2,v 1.6 2011/11/18 19:32:13 each Exp $
; Use comment lines instead of blank lines to combine update requests into
; 9
update add 128.1.zz.3.2.2001.rpz-ip.bl 300 CNAME .
;
-; apply the policy with the lexically smallest address of 192.168.5.1
+; apply the policy with the lexically smaller trigger address of 192.168.5.1
; to an RRset of more than one A RR
; 11
update add 32.1.5.168.192.rpz-ip.bl 300 A 127.0.0.1
send
; prefer QNAME to IP for a5-4.tld2
-; 13
+; 13, 14
update add 32.4.5.168.192.rpz-ip.bl 300 CNAME a12.tld2.
update add a5-4.tld2.bl 300 CNAME a14.tld4.
;
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: test3,v 1.5 2011/10/13 01:32:32 vjs Exp $
; Use comment lines instead of blank lines to combine update requests into
server 10.53.0.3 5300
+; 3, 4, 5
; NXDOMAIN for *.sub1.tld2 by NSDNAME
update add *.sub1.tld2.rpz-nsdname.bl. 300 CNAME .
;
+; 6
; walled garden for *.sub2.tld2
update add *.sub2.tld2.rpz-nsdname.bl. 300 CNAME a12-cname.tld2.
;
+; 7, 8
; 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.
;
+; 9
; prefer QNAME policy to NSDNAME policy
update add a4-1.tld2.bl. 300 A 12.12.12.12
-;
+; 10
; 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
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $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
--- /dev/null
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+
+
+; 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'
+
+; walled-garden NSIP tests
+
+server 10.53.0.3 5300
+
+; rewrite all of tld2 based on its server IP address
+update add 32.2.0.53.10.rpz-nsip.bl. 300 A 41.41.41.41
+update add 32.2.0.53.10.rpz-nsip.bl. 300 AAAA 2041::41
+update add 32.2.0.53.10.rpz-nsip.bl. 300 TXT "NSIP walled garden"
+send
-; Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+; Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
;
; Permission to use, copy, modify, and/or distribute this software for any
; purpose with or without fee is hereby granted, provided that the above
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
; PERFORMANCE OF THIS SOFTWARE.
-; $Id: test5,v 1.5.4.1 2012/02/24 17:22:37 vjs Exp $
; Use comment lines instead of blank lines to combine update requests into
-# Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: tests.sh,v 1.12 2012/01/07 23:46:53 tbox Exp $
# test response policy zones (RPZ)
. $SYSTEMTESTTOP/conf.sh
ns=10.53.0
-ns1=$ns.1 # root, defining the others
-ns2=$ns.2 # server whose answers are rewritten
-ns3=$ns.3 # resolve that does the rewriting
-ns4=$ns.4 # another server that is rewritten
-ns5=$ns.5 # check performance with this server
+ns1=$ns.1 # root, defining the others
+ns2=$ns.2 # authoritative server whose answers are rewritten
+ns3=$ns.3 # resolver that does the rewriting
+ns4=$ns.4 # another authoritative server that is rewritten
+ns5=$ns.5 # resolver to check performance and give un-rewritten
+ # responses for comparisons
HAVE_CORE=
+SAVE_RESULTS=
+NS3_STATS=47
USAGE="$0: [-x]"
while getopts "x" c; do
# really quit on control-C
trap 'exit 1' 1 2 15
+TS='%H:%M:%S '
+TS=
+comment () {
+ if test -n "$TS"; then
+ date "+I:${TS}$*"
+ fi
+}
RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
digcmd () {
- digcmd_args="+noadd +nosearch +time=1 +tries=1 -p 5300 $*"
+ digcmd_args="+noadd +time=1 +tries=1 -p 5300 $*"
expr "$digcmd_args" : '.*@' >/dev/null || \
digcmd_args="$digcmd_args @$ns3"
expr "$digcmd_args" : '.*+[no]*auth' >/dev/null || \
setret () {
ret=1
+ status=`expr $status + 1`
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
+ if ! $NSUPDATE -v $TEST_FILE; then
echo "I:failed to update policy zone with $TEST_FILE"
exit 1
fi
return 1
}
+# check that statistics for $1 in $2 = $3
+ckstats () {
+ $RNDCCMD $1 stats
+ CNT=`sed -n -e 's/[ ]*\([0-9]*\).response policy.*/\1/p' \
+ $2/named.stats`
+ CNT=`expr 0$CNT + 0`
+ if test "$CNT" -ne $3; then
+ setret "I:wrong $2 statistics of $CNT instead of $3"
+ fi
+}
+
# $1=message $2=optional test file name
start_group () {
ret=0
- test -n "$1" && echo "I:checking $1"
+ test -n "$1" && date "+I:${TS}checking $1"
TEST_FILE=$2
if test -n "$TEST_FILE"; then
GROUP_NM="-$TEST_FILE"
end_group () {
if test -n "$TEST_FILE"; then
+ # remove the previous set of test rules
sed -e 's/[ ]add[ ]/ delete /' $TEST_FILE | $NSUPDATE
TEST_FILE=
fi
ckalive $ns3 "I:failed; ns3 server crashed and restarted"
- if test "$status" -eq 0; then
- # look for complaints from rpz.c
- EMSGS=`grep -l 'invalid rpz' */*.run`
- if test -n "$EMSGS"; then
- setret "I:'invalid rpz' complaints in $EMSGS starting with:"
- grep 'invalid rpz' */*.run | sed -e '4,$d' -e 's/^/I: /'
- fi
- # look for complaints from rpz.c and query.c
- EMSGS=`grep -l 'rpz .*failed' */*.run`
- if test -n "$EMSGS"; then
- setret "I:'rpz failed' complaints in $EMSGS starting with:"
- grep 'rpz .*failed' */*.run | sed -e '4,$d' -e 's/^/I: /'
- fi
- fi
- status=`expr $status + $ret`
GROUP_NM=
}
+clean_result () {
+ if test -z "$SAVE_RESULTS"; then
+ rm -f $*
+ fi
+}
+
# $1=dig args $2=other dig output file
ckresult () {
#ckalive "$1" "I:server crashed by 'dig $1'" || return 1
if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then
- rm -f ${DIGNM}*
+ clean_result ${DIGNM}*
return 0
fi
setret "I:'dig $1' wrong; diff $DIGNM $2"
digcmd $2 >$DIGNM
#ckalive "$2" "I:server crashed by 'dig $2'" || return 1
ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'`
- ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\} *\([0-9]*\) IN A\{1,4\} ${ADDR_ESC}\$/\1/p" $DIGNM`
+ ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\} *\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p" $DIGNM`
if test -z "$ADDR_TTL"; then
setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM"
return 1
setret "I:'dig $2' wrong; TTL=$ADDR_TTL instead of $3 in $DIGNM"
return 1
fi
- rm -f ${DIGNM}*
+ clean_result ${DIGNM}*
}
# check that a response is not rewritten
nochange () {
make_dignm
digcmd $* >$DIGNM
- digcmd $* @$ns2 >${DIGNM}_OK
- ckresult "$*" ${DIGNM}_OK && rm -f ${DIGNM}_OK
+ digcmd $* @$ns5 >${DIGNM}_OK
+ ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK
}
# check against a 'here document'
nochange . # 1 do not crash or rewrite root
nxdomain a0-1.tld2 # 2
nodata a3-1.tld2 # 3
-nodata a3-2.tld2 # 4 no crash on DNAME
-nodata sub.a3-2.tld2
+nodata a3-2.tld2 # 4 nodata at DNAME itself
+nochange sub.a3-2.tld2 # 5 miss where DNAME might work
nxdomain a4-2.tld2 # 6 rewrite based on CNAME target
nxdomain a4-2-cname.tld2 # 7
nodata a4-3-cname.tld2 # 8
addr 57.57.57.57 a3-7.sub1.tld2 # 17 wildcard CNAME
addr 127.0.0.16 a4-5-cname3.tld2 # 18 CNAME chain
addr 127.0.0.17 a4-6-cname3.tld2 # 19 stop short in CNAME chain
-nochange a0-1.tld2 +norecurse # 20 check that RD=1 is required
-nochange a3-1.tld2 +norecurse # 21
-nochange a3-2.tld2 +norecurse # 22
-nochange sub.a3-2.tld2 +norecurse # 23
+nochange a5-2.tld2 +norecurse # 20 check that RD=1 is required
+nochange a5-3.tld2 +norecurse # 21
+nochange a5-4.tld2 +norecurse # 22
+nochange sub.a5-4.tld2 +norecurse # 23
nxdomain c1.crash2.tld3 # 24 assert in rbtdb.c
nxdomain a0-1.tld2 +dnssec # 25 simple DO=1 without signatures
-nxdomain a0-1.tld2s # 26 simple DO=0 with signatures
+nxdomain a0-1.tld2s +nodnssec # 26 simple DO=0 with signatures
nochange a0-1.tld2s +dnssec # 27 simple DO=1 with signatures
nxdomain a0-1s-cname.tld2s +dnssec # 28 DNSSEC too early in CNAME chain
nochange a0-1-scname.tld2 +dnssec # 29 DNSSEC on target in CNAME chain
-nochange a0-1.tld2s srv +auth +dnssec # 30 no write for +DNSSEC and no record
-nxdomain a0-1.tld2s srv # 31
+nochange a0-1.tld2s srv +auth +dnssec # 30 no write for DNSSEC and no record
+nxdomain a0-1.tld2s srv +nodnssec # 31
end_group
start_group "IP rewrites" test2
nochange a4-1-aaaa.tld2 -taaaa # 10
addr 127.0.0.1 a5-1-2.tld2 # 11 prefer smallest policy address
addr 127.0.0.1 a5-3.tld2 # 12 prefer first conflicting IP zone
-addr 14.14.14.14 a5-4.tld2 # 13 prefer QNAME to IP
-nochange a5-4.tld2 +norecurse # 14 check that RD=1 is required
+nochange a5-4.tld2 +norecurse # 13 check that RD=1 is required for #14
+addr 14.14.14.14 a5-4.tld2 # 14 prefer QNAME to IP
nochange a4-4.tld2 # 15 PASSTHRU
nxdomain c2.crash2.tld3 # 16 assert in rbtdb.c
end_group
end_group
if ./rpz nsdname; then
+ # these tests assume "min-ns-dots 0"
start_group "NSDNAME rewrites" test3
- nochange a3-1.tld2
+ nochange a3-1.tld2 # 1
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
addr 127.0.0.2 a3-1.subsub.sub3.tld2
nxdomain xxx.crash1.tld2 # 12 dns_db_detachnode() crash
end_group
+ NS3_STATS=`expr $NS3_STATS + 7`
else
- echo "I:NSDNAME not checked; named not configured with --enable-rpz-nsdname"
+ echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
fi
if ./rpz nsip; then
+ # these tests assume "min-ns-dots 0"
start_group "NSIP rewrites" test4
- nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2 by NSIP
+ nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2
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
+
+ start_group "walled garden NSIP rewrites" test4a
+ addr 41.41.41.41 a3-1.tld2 # 1 walled garden for all of tld2
+ addr 2041::41 'a3-1.tld2 AAAA' # 2 walled garden for all of tld2
+ here a3-1.tld2 TXT <<'EOF' # 3 text message for all of tld2
+ ;; status: NOERROR, x
+ a3-1.tld2. x IN TXT "NSIP walled garden"
+EOF
+ end_group
+ NS3_STATS=`expr $NS3_STATS + 4`
else
- echo "I:NSIP not checked; named not configured with --enable-rpz-nsip"
+ echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
fi
# policies in ./test5 overridden by response-policy{} in ns3/named.conf
nocrash www.redirect -t$Q
nocrash www.credirect -t$Q
done
+
+# This is not a bug, because any data leaked by writing 24.4.3.2.10.rpz-ip
+# (or whatever) is available by publishing "foo A 10.2.3.4" and then
+# resolving foo.
+# nxdomain 32.3.2.1.127.rpz-ip
end_group
QPERF=`sh qperf.sh`
if test -n "$QPERF"; then
perf () {
- echo "I:checking performance $1"
- # don't measure the costs of -d99
- $RNDCCMD $ns5 notrace >/dev/null
- $QPERF -1 -l2 -d ns5/requests -s $ns5 -p 5300 >ns5/$2.perf
+ date "+I:${TS}checking performance $1"
+ # Dry run to prime everything
+ comment "before dry run $1"
+ $QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >/dev/null
+ comment "before real test $1"
+ PFILE="ns5/$2.perf"
+ $QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >$PFILE
+ comment "after test $1"
+ X=`sed -n -e 's/.*Returned *\([^ ]*:\) *\([0-9]*\) .*/\1\2/p' $PFILE \
+ | tr '\n' ' '`
+ if test "$X" != "$3"; then
+ setret "I:wrong results '$X' in $PFILE"
+ fi
ckalive $ns5 "I:failed; server #5 crashed"
}
trim () {
sed -n -e 's/.*Queries per second: *\([0-9]*\).*/\1/p' ns5/$1.perf
}
- # Dry run to prime disk cache
- # Otherwise a first test of either flavor is 25% low
- perf 'to prime disk cache' rpz
-
- # get queries/second with rpz
- perf 'with rpz' rpz
+ # get qps with rpz
+ perf 'with rpz' rpz 'NOERROR:2900 NXDOMAIN:100 '
+ RPZ=`trim rpz`
- # turn off rpz and measure queries/second again
- # Don't wait for a clean stop. Clean stops of this server need seconds
- # until the sockets are closed. 5 or 10 seconds after that, the
- # server really stops and deletes named.pid.
+ # turn off rpz and measure qps again
echo "# rpz off" >ns5/rpz-switch
- PID=`cat ns5/named.pid`
- test -z "$PID" || kill -9 "$PID"
- $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5
- perf 'without rpz' norpz
-
- # Don't wait for a clean stop. Clean stops of this server need seconds
- # until the sockets are closed. 5 or 10 seconds after that, the
- # server really stops and deletes named.pid.
- echo "# rpz off" >ns5/rpz-switch
- PID=`cat ns5/named.pid`
- test -z "$PID" || kill -9 "$PID" && rm -f ns5/named.pid
-
+ RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
+ perf 'without rpz' norpz 'NOERROR:3000 '
NORPZ=`trim norpz`
- RPZ=`trim rpz`
- echo "I:$RPZ qps with RPZ versus $NORPZ qps without"
- # fail if RPZ costs more than 100%
- NORPZ2=`expr "$NORPZ" / 2`
- if test "$RPZ" -le "$NORPZ2"; then
- echo "I:rpz $RPZ qps too far below non-RPZ $NORPZ qps"
- status=`expr $status + 1`
+ PERCENT=`expr \( "$RPZ" \* 100 + \( $NORPZ / 2 \) \) / $NORPZ`
+ echo "I:$RPZ qps with rpz is $PERCENT% of $NORPZ qps without rpz"
+
+ MIN_PERCENT=30
+ if test "$PERCENT" -lt $MIN_PERCENT; then
+ setret "I:$RPZ qps with rpz or $PERCENT% is below $MIN_PERCENT% of $NORPZ qps"
+ fi
+
+ if test "$PERCENT" -ge 100; then
+ setret "I:$RPZ qps with RPZ or $PERCENT% of $NORPZ qps without RPZ is too high"
fi
+
+ ckstats $ns5 ns5 203
+
else
echo "I:performance not checked; queryperf not available"
fi
+ckstats $ns3 ns3 $NS3_STATS
+
# restart the main test RPZ server to see if that creates a core file
if test -z "$HAVE_CORE"; then
$PERL $SYSTEMTESTTOP/stop.pl . ns3
test -z "$HAVE_CORE" || setret "I:found $HAVE_CORE; memory leak?"
fi
+# look for complaints from lib/dns/rpz.c and bin/name/query.c
+EMSGS=`egrep -l 'invalid rpz|rpz.*failed' ns*/named.run`
+if test -n "$EMSGS"; then
+ setret "I:error messages in $EMSGS starting with:"
+ egrep 'invalid rpz|rpz.*failed' ns*/named.run | sed -e '10,$d' -e 's/^/I: /'
+fi
echo "I:exit status: $status"
exit $status
[default=autodetect]
--enable-fixed-rrset enable fixed rrset ordering
[default=no]
- --enable-rpz-nsip enable rpz-nsip rules [default=no]
- --enable-rpz-nsdname enable rpz-nsdname rules [default=no]
+ --disable-rpz-nsip disable rpz-nsip rules [default=enabled]
+ --disable-rpz-nsdname disable rpz-nsdname rules [default=enabled]
--enable-filter-aaaa enable filtering of AAAA records
[default=no]
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
test "${with_atf+set}" = set || with_atf=yes
test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
- test "${enable_rpz_nsip+set}" = set || enable_rpz_nsip=yes
- test "${enable_rpz_nsdname+set}" = set || enable_rpz_nsdname=yes
test "${with_dlz_filesystem+set}" = set || with_dlz_filesystem=yes
case "$host" in
*-darwin*)
if test "${enable_rpz_nsip+set}" = set; then :
enableval=$enable_rpz_nsip; enable_nsip="$enableval"
else
- enable_nsip="no"
+ enable_nsip="yes"
fi
case "$enable_nsip" in
if test "${enable_rpz_nsdname+set}" = set; then :
enableval=$enable_rpz_nsdname; enable_nsdname="$enableval"
else
- enable_nsdname="no"
+ enable_nsdname="yes"
fi
case "$enable_nsdname" in
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
test "${with_atf+set}" = set || with_atf=yes
test "${enable_filter_aaaa+set}" = set || enable_filter_aaaa=yes
- test "${enable_rpz_nsip+set}" = set || enable_rpz_nsip=yes
- test "${enable_rpz_nsdname+set}" = set || enable_rpz_nsdname=yes
test "${with_dlz_filesystem+set}" = set || with_dlz_filesystem=yes
case "$host" in
*-darwin*)
# Enable response policy rewriting using NS IP addresses
#
AC_ARG_ENABLE(rpz-nsip,
- [ --enable-rpz-nsip enable rpz-nsip rules [[default=no]]],
+ [ --disable-rpz-nsip disable rpz-nsip rules [[default=enabled]]],
enable_nsip="$enableval",
- enable_nsip="no")
+ enable_nsip="yes")
case "$enable_nsip" in
yes)
AC_DEFINE(ENABLE_RPZ_NSIP, 1,
# Enable response policy rewriting using NS name
#
AC_ARG_ENABLE(rpz-nsdname,
- [ --enable-rpz-nsdname enable rpz-nsdname rules [[default=no]]],
+ [ --disable-rpz-nsdname disable rpz-nsdname rules [[default=enabled]]],
enable_nsdname="$enableval",
- enable_nsdname="no")
+ enable_nsdname="yes")
case "$enable_nsdname" in
yes)
AC_DEFINE(ENABLE_RPZ_NSDNAME, 1,
<optional> policy given | disabled | passthru | nxdomain | nodata | cname <replaceable>domain</replaceable> </optional>
<optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional> ;
} <optional> recursive-only <replaceable>yes_or_no</replaceable> </optional> <optional> max-policy-ttl <replaceable>number</replaceable> </optional>
- <optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional> ; </optional>
+ <optional> break-dnssec <replaceable>yes_or_no</replaceable> </optional> <optional> min-ns-dots <replaceable>number</replaceable> </optional>
+ <optional> qname-wait-recurse <replaceable>yes_or_no</replaceable> </optional> ; </optional>
};
</programlisting>
Response policy zones are named in the
<command>response-policy</command> 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
+ Response policy zones are ordinary DNS zones containing RRsets
that can be queried normally if allowed.
It is usually best to restrict those queries with something like
<command>allow-query { localhost; };</command>.
They are encoded as subdomains of
<userinput>rpz-nsdomain</userinput> relativized
to the RPZ origin name.
- </para>
-
- <para>
NSIP triggers match IP addresses in A and
AAAA RRsets for domains that can be checked against NSDNAME
policy records.
NSIP triggers are encoded like IP triggers except as subdomains of
<userinput>rpz-nsip</userinput>.
+ NSDNAME and NSIP triggers are checked only for names with at
+ least <command>min-ns-dots</command> dots.
+ The default value of <command>min-ns-dots</command> is 1 to
+ exclude top level domains.
</para>
<para>
- The query response is checked against all RPZs, so
+ The query response is checked against all response policy zones, so
two or more policy records can be triggered by a response.
Because DNS responses can be rewritten according to at most one
policy record, a single record encoding an action (other than
When the processing of a response is restarted to resolve
DNAME or CNAME records and a policy record set has
not been triggered,
- all RPZs are again consulted for the DNAME or CNAME names
- and addresses.
- </para>
-
- <para>
- 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 <acronym>BIND</acronym> is built with the
- <userinput>--enable-rpz-nsip</userinput> or
- <userinput>--enable-rpz-nsdname</userinput> options
- on the "configure" command line.
+ all response policy zones are again consulted for the
+ DNAME or CNAME names and addresses.
</para>
<para>
</para>
<para>
- The actions specified in an RPZ can be overridden with a
+ The actions specified in a policy zone can be overridden with a
<command>policy</command> clause in the
<command>response-policy</command> option.
- An organization using an RPZ provided by another organization might
- use this mechanism to redirect domains to its own walled garden.
+ An organization using a policy zone provided by another
+ organization might use this mechanism to redirect domains
+ to its own walled garden.
<itemizedlist>
<listitem><command>GIVEN</command> says "do not override but
perform the action specified in the zone."
</para>
<para>
- By default, the actions encoded in an RPZ are applied
- only to queries that ask for recursion (RD=1).
- That default can be changed for a single RPZ or all RPZs in a view
+ By default, the actions encoded in a response policy zone
+ are applied only to queries that ask for recursion (RD=1).
+ That default can be changed for a single policy zone or
+ all response policy zones in a view
with a <command>recursive-only no</command> clause.
This feature is useful for serving the same zone files
both inside and outside an RFC 1918 cloud and using RPZ to
</para>
<para>
- Also by default, RPZ actions are applied only to DNS requests that
- either do not request DNSSEC metadata (DO=0) or when no DNSSEC
- records are available for request name in the original zone (not
- the response policy zone).
- This default can be changed for all RPZs in a view with a
- <command>break-dnssec yes</command> clause.
- In that case, RPZ actions are applied regardless of DNSSEC.
- The name of the clause option reflects the fact that results
- rewritten by RPZ actions cannot verify.
+ Also by default, RPZ actions are applied only to DNS requests
+ that either do not request DNSSEC metadata (DO=0) or when no
+ DNSSEC records are available for request name in the original
+ zone (not the response policy zone). This default can be
+ changed for all response policy zones in a view with a
+ <command>break-dnssec yes</command> clause. In that case, RPZ
+ actions are applied regardless of DNSSEC. The name of the
+ clause option reflects the fact that results rewritten by RPZ
+ actions cannot verify.
+ </para>
+
+ <para>
+ No DNS records are needed to trigger a QNAME action. The name
+ itself is sufficient, so in principle the query name need not
+ be recursively resolved. However, not resolving the requested
+ name leaks the fact that response policy rewriting is in use
+ and that the name is listed in a policy zone to operators of
+ servers for listed names. To prevent that information leak, by
+ default any recursion needed for a request is done before any
+ policy triggers are considered. Because listed domains often
+ have slow authoritative servers, this default behavior can cost
+ significant time. The <command>qname-wait-recurse no</command>
+ option overrides the default behavior when recursion cannot
+ change the response. <command>qname-wait-recurse no</command>
+ does not affect QNAME triggers in policy zones listed after
+ other zones containing IP, NSIP and NSDNAME triggers, because
+ those may depend on the A, AAAA, and NS records that would be
+ found during recursive resolution. It also does not affect
+ DNSSEC requests (DO=1) unless <command>break-dnssec yes</command>
+ is in use, because the response would depend on whether or not
+ RRSIG records were found during resolution.
</para>
<para>
ns.domain.com.rpz-nsdname CNAME .
48.zz.2.2001.rpz-nsip CNAME .
</programlisting>
- <para>
- Note: RPZ may impact server performance. Each configured
- response policy zone requires the server to perform one to four
- additional database lookups before a query can be answered.
+ <para>
+ RPZ can affect server performance.
+ Each configured response policy zone requires the server to
+ perform one to four additional database lookups before a
+ query can be answered.
For example, a DNS server with four policy zones, each with all
- four kinds of response triggers — QNAME, IP, NSIP, and
- NSDNAME — requires a total of 17 times as many database
+ four kinds of response triggers, QNAME, IP, NSIP, and
+ NSDNAME, requires a total of 17 times as many database
lookups as a similar DNS server with no response policy zones.
A <acronym>BIND9</acronym> server with adequate memory and one
response policy zone with QNAME and IP triggers might achieve a
- maximum queries-per-second rate about 20% lower. A server with
- four response policy zones with QNAME and IP triggers might
- have a maximum QPS rate about 50% lower.
- </para>
+ maximum queries-per-second rate about 20% lower.
+ A server with four response policy zones with QNAME and IP
+ triggers might have a maximum QPS rate about 50% lower.
+ </para>
+
+ <para>
+ Responses rewritten by RPZ are counted in the
+ <command>RPZRewrites</command> statistics.
+ </para>
</sect3>
<sect3>
</para>
</entry>
</row>
+ <row rowsep="0">
+ <entry colname="1">
+ <para><command>RPZRewrites</command></para>
+ </entry>
+ <entry colname="2">
+ <para><command></command></para>
+ </entry>
+ <entry colname="3">
+ <para>
+ Response policy zone rewrites.
+ </para>
+ </entry>
+ </row>
</tbody>
</tgroup>
</informaltable>
(db->methods->resigned)(db, rdataset, version);
}
+/*
+ * Attach a database to policy zone databases.
+ * This should only happen when the caller has already ensured that
+ * it is dealing with a database that understands response policy zones.
+ */
void
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
-{
- if (db->methods->rpz_enabled != NULL)
- (db->methods->rpz_enabled)(db, st);
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ REQUIRE(db->methods->rpz_attach != NULL);
+ (db->methods->rpz_attach)(db, rpzs, rpz_num);
}
-void
-dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
- dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
- dns_rdataset_t *ardataset, dns_rpz_st_t *st,
- dns_name_t *query_qname)
-{
- if (db->methods->rpz_findips != NULL)
- (db->methods->rpz_findips)(rpz, rpz_type, zone, db, version,
- ardataset, st, query_qname);
+/*
+ * Finish loading a response policy zone.
+ */
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db) {
+ if (db->methods->rpz_ready == NULL)
+ return (ISC_R_SUCCESS);
+ return ((db->methods->rpz_ready)(db));
}
NULL, /* resigned */
NULL, /* isdnssec */
NULL, /* getrrsetstats */
- NULL, /* rpz_enabled */
- NULL, /* rpz_findips */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
NULL, /* findnodeext */
NULL, /* findext */
NULL, /* setcachestats */
dns_dbversion_t *version);
isc_boolean_t (*isdnssec)(dns_db_t *db);
dns_stats_t *(*getrrsetstats)(dns_db_t *db);
- void (*rpz_enabled)(dns_db_t *db, dns_rpz_st_t *st);
- void (*rpz_findips)(dns_rpz_zone_t *rpz,
- dns_rpz_type_t rpz_type,
- dns_zone_t *zone, dns_db_t *db,
- dns_dbversion_t *version,
- dns_rdataset_t *ardataset,
- dns_rpz_st_t *st,
- dns_name_t *query_qname);
+ void (*rpz_attach)(dns_db_t *db, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num);
+ isc_result_t (*rpz_ready)(dns_db_t *db);
isc_result_t (*findnodeext)(dns_db_t *db, dns_name_t *name,
isc_boolean_t create,
dns_clientinfomethods_t *methods,
*/
void
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st);
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
/*%<
- * See if a policy database has DNS_RPZ_TYPE_IP, DNS_RPZ_TYPE_NSIP, or
- * DNS_RPZ_TYPE_NSDNAME records.
+ * Attach the response policy information for a view to a database for a
+ * zone for the view.
*/
-void
-dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
- dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
- dns_rdataset_t *ardataset, dns_rpz_st_t *st,
- dns_name_t *query_qname);
+isc_result_t
+dns_db_rpz_ready(dns_db_t *db);
/*%<
- * 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.
- *
- * Requires:
- * \li search in policy zone 'rpz' for a match of 'rpz_type' either
- * DNS_RPZ_TYPE_IP or DNS_RPZ_TYPE_NSIP
- * \li 'zone' and 'db' are the database corresponding to 'rpz'
- * \li 'version' is the required version of the database
- * \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
+ * Finish loading a response policy zone.
*/
ISC_LANG_ENDDECLS
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rpz.h,v 1.5 2011/10/28 11:46:50 marka Exp $ */
#ifndef DNS_RPZ_H
#define DNS_RPZ_H 1
#include <dns/fixedname.h>
#include <dns/rdata.h>
#include <dns/types.h>
+#include <isc/refcount.h>
ISC_LANG_BEGINDECLS
-#define DNS_RPZ_IP_ZONE "rpz-ip"
-#define DNS_RPZ_NSIP_ZONE "rpz-nsip"
-#define DNS_RPZ_NSDNAME_ZONE "rpz-nsdname"
-#define DNS_RPZ_PASSTHRU_ZONE "rpz-passthru"
+#define DNS_RPZ_PREFIX "rpz-"
+#define DNS_RPZ_IP_ZONE DNS_RPZ_PREFIX"ip"
+#define DNS_RPZ_NSIP_ZONE DNS_RPZ_PREFIX"nsip"
+#define DNS_RPZ_NSDNAME_ZONE DNS_RPZ_PREFIX"nsdname"
+#define DNS_RPZ_PASSTHRU_ZONE DNS_RPZ_PREFIX"passthru"
-typedef isc_uint8_t dns_rpz_cidr_bits_t;
+typedef isc_uint8_t dns_rpz_prefix_t;
typedef enum {
DNS_RPZ_TYPE_BAD,
*/
typedef enum {
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_DISABLED = 1, /* log what would have happened */
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_ERROR
} dns_rpz_policy_t;
+typedef isc_uint8_t dns_rpz_num_t;
+
+#define DNS_RPZ_MAX_ZONES 32
+#if DNS_RPZ_MAX_ZONES > 32
+# if DNS_RPZ_MAX_ZONES > 64
+# error "rpz zone bit masks must fit in a word"
+# endif
+typedef isc_uint64_t dns_rpz_zbits_t;
+#else
+typedef isc_uint32_t dns_rpz_zbits_t;
+#endif
+
+#define DNS_RPZ_ALL_ZBITS ((dns_rpz_zbits_t)-1)
+
+#define DNS_RPZ_INVALID_NUM DNS_RPZ_MAX_ZONES
+
+#define DNS_RPZ_ZBIT(n) (((dns_rpz_zbits_t)1) << (dns_rpz_num_t)(n))
+
/*
- * Specify a response policy zone.
+ * Mask of the specified and higher numbered policy zones
+ * Avoid hassles with (1<<33) or (1<<65)
*/
-typedef struct dns_rpz_zone dns_rpz_zone_t;
+#define DNS_RPZ_ZMASK(n) ((dns_rpz_zbits_t)((((n) >= DNS_RPZ_MAX_ZONES-1) ? \
+ 0 : (1<<((n)+1))) -1))
+/*
+ * A single response policy zone.
+ */
+typedef struct dns_rpz_triggers dns_rpz_triggers_t;
+struct dns_rpz_triggers {
+ int qname;
+ int ipv4;
+ int ipv6;
+ int nsdname;
+ int nsipv4;
+ int nsipv6;
+};
+typedef struct dns_rpz_zone dns_rpz_zone_t;
struct dns_rpz_zone {
- ISC_LINK(dns_rpz_zone_t) link;
- int num; /* ordinal in list of policy zones */
- dns_name_t origin; /* Policy zone name */
- dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */
- dns_name_t passthru;/* DNS_RPZ_PASSTHRU_ZONE. */
- dns_name_t cname; /* override value for ..._CNAME */
- dns_ttl_t max_policy_ttl;
- dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */
- isc_boolean_t recursive_only;
+ isc_refcount_t refs;
+ dns_rpz_num_t num; /* ordinal in list of policy zones */
+ dns_name_t origin; /* Policy zone name */
+ dns_name_t ip; /* DNS_RPZ_IP_ZONE.origin. */
+ dns_name_t nsdname; /* DNS_RPZ_NSDNAME_ZONE.origin */
+ dns_name_t nsip; /* DNS_RPZ_NSIP_ZONE.origin. */
+ dns_name_t passthru; /* DNS_RPZ_PASSTHRU_ZONE. */
+ dns_name_t cname; /* override value for ..._CNAME */
+ dns_ttl_t max_policy_ttl;
+ dns_rpz_policy_t policy; /* DNS_RPZ_POLICY_GIVEN or override */
+ dns_rpz_triggers_t triggers;
};
/*
- * Radix trees for response policy IP addresses.
+ * Radix tree node for response policy IP addresses
*/
-typedef struct dns_rpz_cidr dns_rpz_cidr_t;
+typedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t;
+
+/*
+ * Response policy zones known to a view.
+ */
+typedef struct dns_rpz_zones dns_rpz_zones_t;
+struct dns_rpz_zones {
+ struct {
+ dns_rpz_zbits_t no_rd_ok;
+ isc_boolean_t break_dnssec;
+ isc_boolean_t qname_wait_recurse;
+ unsigned int min_ns_labels;
+ dns_rpz_num_t num_zones;
+ } p;
+ dns_rpz_zone_t *zones[DNS_RPZ_MAX_ZONES];
+
+ dns_rpz_zbits_t defined;
+
+ /*
+ * The set of records for a policy zone are in one of these states:
+ * never loaded load_begun=0 have=0
+ * during initial loading load_begun=1 have=0
+ * and rbtdb->rpzsp == rbtdb->load_rpzsp
+ * after good load load_begun=1 have!=0
+ * after failed initial load load_begun=1 have=0
+ * and rbtdb->load_rpzsp == NULL
+ * reloading after failure load_begun=1 have=0
+ * reloading after success
+ * main rpzs load_begun=1 have!=0
+ * load rpzs load_begun=1 have=0
+ */
+ dns_rpz_zbits_t load_begun;
+ struct {
+ dns_rpz_zbits_t qname;
+ dns_rpz_zbits_t ipv4;
+ dns_rpz_zbits_t ipv6;
+ dns_rpz_zbits_t ip;
+ dns_rpz_zbits_t nsdname;
+ dns_rpz_zbits_t nsipv4;
+ dns_rpz_zbits_t nsipv6;
+ dns_rpz_zbits_t nsip;
+ dns_rpz_zbits_t qname_skip_recurse;
+ } have;
+
+ isc_mem_t *mctx;
+ isc_refcount_t refs;
+ /*
+ * One lock for short term read-only search that guarantees the
+ * consistency of the pointers.
+ * A second lock for maintenance that guarantees no other thread
+ * is adding or deleting nodes.
+ */
+ isc_mutex_t search_lock;
+ isc_mutex_t maint_lock;
+
+ dns_rpz_cidr_node_t *cidr;
+ dns_rbt_t *rbt;
+};
+
/*
* context for finding the best policy
# 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_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.
*/
struct {
dns_rpz_type_t type;
dns_rpz_zone_t *rpz;
- dns_rpz_cidr_bits_t prefix;
+ dns_rpz_prefix_t prefix;
dns_rpz_policy_t policy;
dns_ttl_t ttl;
isc_result_t result;
dns_rdataset_t *sigrdataset;
dns_rdatatype_t qtype;
} q;
- dns_name_t *qname;
+ /*
+ * p_name: current policy owner name
+ * r_name: recursing for this name to possible policy triggers
+ * f_name: saved found name from before recursion
+ */
+ dns_name_t *p_name;
dns_name_t *r_name;
dns_name_t *fname;
- dns_fixedname_t _qnamef;
+ dns_fixedname_t _p_namef;
dns_fixedname_t _r_namef;
dns_fixedname_t _fnamef;
} dns_rpz_st_t;
const char *
dns_rpz_policy2str(dns_rpz_policy_t policy);
-void
-dns_rpz_set_need(isc_boolean_t need);
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
+ dns_name_t *selfname);
-isc_boolean_t
-dns_rpz_needed(void);
+isc_result_t
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx);
void
-dns_rpz_cidr_free(dns_rpz_cidr_t **cidr);
+dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target);
void
-dns_rpz_view_destroy(dns_view_t *view);
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp);
isc_result_t
-dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
- dns_rpz_cidr_t **rbtdb_cidr);
-void
-dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st);
+dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
+ dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
-void
-dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+ dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num);
+
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
void
-dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
-isc_result_t
-dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
- dns_rpz_type_t type, dns_name_t *canon_name,
- dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix);
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+ dns_name_t *ip_name, dns_rpz_prefix_t *prefixp);
-dns_rpz_policy_t
-dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
- dns_name_t *selfname);
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_name_t *trig_name);
ISC_LANG_ENDDECLS
dns_acl_t * aaaa_acl;
dns_dns64list_t dns64;
unsigned int dns64cnt;
- ISC_LIST(dns_rpz_zone_t) rpz_zones;
- isc_boolean_t rpz_recursive_only;
- isc_boolean_t rpz_break_dnssec;
+ dns_rpz_zones_t *rpzs;
dns_dlzdblist_t dlz_searched;
dns_dlzdblist_t dlz_unsearched;
#include <dns/master.h>
#include <dns/masterdump.h>
#include <dns/rdatastruct.h>
+#include <dns/rpz.h>
#include <dns/types.h>
#include <dns/zt.h>
* The array and its contents need to be freed using isc_mem_free.
*/
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num);
+/*%
+ * Set the response policy associated with a zone.
+ */
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone);
+
ISC_LANG_ENDDECLS
dns_rbt_t * tree;
dns_rbt_t * nsec;
dns_rbt_t * nsec3;
- dns_rpz_cidr_t * rpz_cidr;
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_num_t rpz_num;
+ dns_rpz_zones_t *load_rpzs;
/* Unlocked */
unsigned int quantum;
isc_stats_detach(&rbtdb->cachestats);
#ifdef BIND9
- if (rbtdb->rpz_cidr != NULL)
- dns_rpz_cidr_free(&rbtdb->rpz_cidr);
+ if (rbtdb->load_rpzs != NULL) {
+ /*
+ * We must be cleaning up after a failed zone loading.
+ */
+ REQUIRE(rbtdb->rpzs != NULL &&
+ rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&rbtdb->load_rpzs);
+ }
+ if (rbtdb->rpzs != NULL) {
+ REQUIRE(rbtdb->rpz_num < rbtdb->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&rbtdb->rpzs);
+ }
#endif
isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
switch (node->nsec) {
case DNS_RBT_NSEC_NORMAL:
#ifdef BIND9
- if (rbtdb->rpz_cidr != NULL) {
+ if (rbtdb->rpzs != NULL) {
dns_fixedname_init(&fname);
name = dns_fixedname_name(&fname);
dns_rbt_fullnamefromnode(node, name);
- dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
+ dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
}
#endif
result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_CACHE,
ISC_LOG_WARNING,
- "delete_nsecnode(): "
+ "delete_node(): "
"dns_rbt_deletenode(nsecnode): %s",
isc_result_totext(result));
}
}
result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
#ifdef BIND9
- dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
+ if (rbtdb->rpzs != NULL)
+ dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
#endif
break;
case DNS_RBT_NSEC_NSEC:
DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_CACHE,
ISC_LOG_WARNING,
- "delete_nsecnode(): "
+ "delete_node(): "
"dns_rbt_deletenode: %s",
isc_result_totext(result));
}
result = dns_rbt_addnode(tree, name, &node);
if (result == ISC_R_SUCCESS) {
#ifdef BIND9
- if (tree == rbtdb->tree && rbtdb->rpz_cidr != NULL) {
+ if (rbtdb->rpzs != NULL && tree == rbtdb->tree) {
dns_fixedname_t fnamef;
dns_name_t *fname;
dns_fixedname_init(&fnamef);
fname = dns_fixedname_name(&fnamef);
dns_rbt_fullnamefromnode(node, fname);
- dns_rpz_cidr_addip(rbtdb->rpz_cidr, fname);
+ result = dns_rpz_add(rbtdb->rpzs,
+ rbtdb->rpz_num, fname);
}
#endif
dns_rbt_namefromnode(node, &nodename);
return (result);
}
+#ifdef BIND9
/*
- * Mark a database for response policy rewriting.
+ * Connect this RBTDB to the response policy zone summary data for the view.
*/
-#ifdef BIND9
static void
-get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
-{
- dns_rbtdb_t *rbtdb;
+rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ dns_rbtdb_t * rbtdb;
rbtdb = (dns_rbtdb_t *)db;
REQUIRE(VALID_RBTDB(rbtdb));
- RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
- dns_rpz_enabled(rbtdb->rpz_cidr, st);
- RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ REQUIRE(rbtdb->rpzs == NULL && rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
+ dns_rpz_attach_rpzs(rpzs, &rbtdb->rpzs);
+ rbtdb->rpz_num = rpz_num;
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
}
/*
- * 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 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.
+ * Enable this RBTDB as a response policy zone.
*/
-static void
-rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
- dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
- dns_rdataset_t *ardataset, dns_rpz_st_t *st,
- dns_name_t *query_qname)
-{
- dns_rbtdb_t *rbtdb;
- struct in_addr ina;
- struct in6_addr in6a;
- isc_netaddr_t netaddr;
- dns_fixedname_t selfnamef, qnamef;
- dns_name_t *selfname, *qname;
- dns_rbtnode_t *node;
- dns_rdataset_t zrdataset;
- dns_rpz_cidr_bits_t prefix;
+static isc_result_t
+rpz_ready(dns_db_t *db) {
+ dns_rbtdb_t * rbtdb;
isc_result_t result;
- dns_rpz_policy_t rpz_policy;
- dns_ttl_t ttl;
rbtdb = (dns_rbtdb_t *)db;
REQUIRE(VALID_RBTDB(rbtdb));
- RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-
- if (rbtdb->rpz_cidr == NULL) {
- RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
- return;
- }
-
- dns_fixedname_init(&selfnamef);
- dns_fixedname_init(&qnamef);
- selfname = dns_fixedname_name(&selfnamef);
- qname = dns_fixedname_name(&qnamef);
-
- for (result = dns_rdataset_first(ardataset);
- result == ISC_R_SUCCESS;
- result = dns_rdataset_next(ardataset)) {
- dns_rdata_t rdata = DNS_RDATA_INIT;
- dns_rdataset_current(ardataset, &rdata);
- switch (rdata.type) {
- case dns_rdatatype_a:
- INSIST(rdata.length == 4);
- memcpy(&ina.s_addr, rdata.data, 4);
- isc_netaddr_fromin(&netaddr, &ina);
- break;
- case dns_rdatatype_aaaa:
- INSIST(rdata.length == 16);
- memcpy(in6a.s6_addr, rdata.data, 16);
- isc_netaddr_fromin6(&netaddr, &in6a);
- break;
- default:
- continue;
- }
-
- result = dns_rpz_cidr_find(rbtdb->rpz_cidr, &netaddr, rpz_type,
- selfname, qname, &prefix);
- if (result != ISC_R_SUCCESS)
- continue;
-
- /*
- * 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 &&
- 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
- * the prefix of the entry we had before. Find the node
- * corresponding to CDIR tree entry.
- */
- node = NULL;
- result = dns_rbt_findnode(rbtdb->tree, qname, NULL,
- &node, NULL, 0, NULL, NULL);
- if (result != ISC_R_SUCCESS) {
- char namebuf[DNS_NAME_FORMATSIZE];
-
- dns_name_format(qname, namebuf, sizeof(namebuf));
- isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
- DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
- "rpz_findips findnode(%s) failed: %s",
- namebuf, isc_result_totext(result));
- continue;
- }
- /*
- * First look for a simple rewrite of the IP address.
- * If that fails, look for a CNAME. If we cannot find
- * a CNAME or the CNAME is neither of the special forms
- * "*" or ".", treat it like a real CNAME.
- */
- dns_rdataset_init(&zrdataset);
- result = dns_db_findrdataset(db, node, version, ardataset->type,
- 0, 0, &zrdataset, NULL);
- if (result != ISC_R_SUCCESS)
- result = dns_db_findrdataset(db, node, version,
- dns_rdatatype_cname,
- 0, 0, &zrdataset, NULL);
- if (result == ISC_R_SUCCESS) {
- if (zrdataset.type != dns_rdatatype_cname) {
- rpz_policy = DNS_RPZ_POLICY_RECORD;
- } else {
- rpz_policy = dns_rpz_decode_cname(rpz,
- &zrdataset,
- selfname);
- if (rpz_policy == DNS_RPZ_POLICY_RECORD ||
- rpz_policy == DNS_RPZ_POLICY_WILDCNAME)
- result = DNS_R_CNAME;
- }
- ttl = zrdataset.ttl;
- } else {
- rpz_policy = DNS_RPZ_POLICY_RECORD;
- result = DNS_R_NXRRSET;
- ttl = DNS_RPZ_TTL_DEFAULT;
- }
-
- /*
- * Use an overriding action specified in the configuration file
- */
- 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;
- }
-
- 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 = ISC_MIN(ttl, rpz->max_policy_ttl);
- st->m.result = result;
- dns_name_copy(qname, st->qname, NULL);
- if ((rpz_policy == DNS_RPZ_POLICY_RECORD ||
- 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);
- st->m.version = version;
- dns_zone_attach(zone, &st->m.zone);
- if (dns_rdataset_isassociated(&zrdataset))
- dns_rdataset_disassociate(&zrdataset);
+ RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ if (rbtdb->rpzs == NULL) {
+ INSIST(rbtdb->rpz_num == DNS_RPZ_INVALID_NUM);
+ result = ISC_R_SUCCESS;
+ } else {
+ result = dns_rpz_ready(rbtdb->rpzs, &rbtdb->load_rpzs,
+ rbtdb->rpz_num);
}
-
- RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+ RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_write);
+ return (result);
}
#endif
noderesult = dns_rbt_addnode(rbtdb->tree, name, nodep);
#ifdef BIND9
- if (noderesult == ISC_R_SUCCESS)
- dns_rpz_cidr_addip(rbtdb->rpz_cidr, name);
+ if (rbtdb->rpzs != NULL && noderesult == ISC_R_SUCCESS)
+ noderesult = dns_rpz_add(rbtdb->load_rpzs, rbtdb->rpz_num,
+ name);
#endif
if (!hasnsec)
RBTDB_LOCK(&rbtdb->lock, isc_rwlocktype_write);
+#ifdef BIND9
+ if (rbtdb->rpzs != NULL) {
+ isc_result_t result;
+
+ result = dns_rpz_beginload(&rbtdb->load_rpzs,
+ rbtdb->rpzs, rbtdb->rpz_num);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(rbtdb->common.mctx, loadctx,
+ sizeof(*loadctx));
+ return (result);
+ }
+ }
+#endif
+
REQUIRE((rbtdb->attributes & (RBTDB_ATTR_LOADED|RBTDB_ATTR_LOADING))
== 0);
rbtdb->attributes |= RBTDB_ATTR_LOADING;
isdnssec,
NULL,
#ifdef BIND9
- get_rpz_enabled,
- rpz_findips,
+ rpz_attach,
+ rpz_ready,
#else
NULL,
NULL,
return (result);
}
-#ifdef BIND9
- /*
- * Get ready for response policy IP address searching if at least one
- * zone has been configured as a response policy zone and this
- * is not a cache zone.
- * It would be better to know that this database is for a policy
- * zone named for a view, but that would require knowledge from
- * above such as an argv[] set from data in the zone.
- */
- if (type == dns_dbtype_zone && !dns_name_equal(origin, dns_rootname)) {
- result = dns_rpz_new_cidr(mctx, origin, &rbtdb->rpz_cidr);
- if (result != ISC_R_SUCCESS) {
- free_rbtdb(rbtdb, ISC_FALSE, NULL);
- return (result);
- }
- }
-#endif
-
/*
* In order to set the node callback bit correctly in zone databases,
* we need to know if the node has the origin name of the zone.
}
rbtdb->attributes = 0;
rbtdb->task = NULL;
+ rbtdb->rpzs = NULL;
+ rbtdb->load_rpzs = NULL;
+ rbtdb->rpz_num = DNS_RPZ_INVALID_NUM;
/*
* Version Initialization.
/*
- * Copyright (C) 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id$ */
/*! \file */
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
+#include <dns/rbt.h>
#include <dns/rpz.h>
#include <dns/view.h>
/*
* Parallel radix trees for databases of response policy IP addresses
*
- * The radix or Patricia trees are somewhat specialized to handle response
- * policy addresses by representing the two test of IP IP addresses and name
- * server IP addresses in a single tree.
+ * The radix or patricia trees are somewhat specialized to handle response
+ * policy addresses by representing the two sets of IP addresses and name
+ * server IP addresses in a single tree. One set of IP addresses is
+ * for rpz-ip policies or policies triggered by addresses in A or
+ * AAAA records in responses.
+ * The second set is for rpz-nsip policies or policies triggered by addresses
+ * in A or AAAA records for NS records that are authorities for responses.
*
* Each leaf indicates that an IP address is listed in the IP address or the
* name server IP address policy sub-zone (or both) of the corresponding
* tree, the node in the policy zone's database is found by converting
* the IP address to a domain name in a canonical form.
*
- * The response policy zone canonical form of IPv6 addresses is one of:
+ *
+ * The response policy zone canonical form of an IPv6 address is one of:
* prefix.W.W.W.W.W.W.W.W
* prefix.WORDS.zz
* prefix.WORDS.zz.WORDS
* prefix is the prefix length of the address between 1 and 32
* B is a number between 0 and 255
*
- * IPv4 addresses are distinguished from IPv6 addresses by having
+ * Names for IPv4 addresses are distinguished from IPv6 addresses by having
* 5 labels all of which are numbers, and a prefix between 1 and 32.
*/
} dns_rpz_cidr_key_t;
#define ADDR_V4MAPPED 0xffff
+#define KEY_IS_IPV4(prefix,ip) ((prefix) >= 96 && (ip)->w[0] == 0 && \
+ (ip)->w[1] == 0 && (ip)->w[2] == ADDR_V4MAPPED)
+
+#define DNS_RPZ_WORD_MASK(b) ((b) == 0 ? (dns_rpz_cidr_word_t)(-1) \
+ : ((dns_rpz_cidr_word_t)(-1) \
+ << (DNS_RPZ_CIDR_WORD_BITS - (b))))
-#define DNS_RPZ_WORD_MASK(b) \
- ((b) == 0 ? (dns_rpz_cidr_word_t)(-1) \
- : ((dns_rpz_cidr_word_t)(-1) \
- << (DNS_RPZ_CIDR_WORD_BITS - (b))))
+/*
+ * Get bit #n from the array of words of an IP address.
+ */
+#define DNS_RPZ_IP_BIT(ip, n) (1 & ((ip)->w[(n)/DNS_RPZ_CIDR_WORD_BITS] >> \
+ (DNS_RPZ_CIDR_WORD_BITS \
+ - 1 - ((n) % DNS_RPZ_CIDR_WORD_BITS))))
-#define DNS_RPZ_IP_BIT(ip, bitno) \
- (1 & ((ip)->w[(bitno)/DNS_RPZ_CIDR_WORD_BITS] >> \
- (DNS_RPZ_CIDR_WORD_BITS - 1 - ((bitno) % DNS_RPZ_CIDR_WORD_BITS))))
+/*
+ * A pair of arrays of bits flagging the existence of
+ * IP or QNAME (d) and NSIP or NSDNAME (ns) policy triggers.
+ */
+typedef struct dns_rpz_pair_zbits dns_rpz_pair_zbits_t;
+struct dns_rpz_pair_zbits {
+ dns_rpz_zbits_t d;
+ dns_rpz_zbits_t ns;
+};
-typedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t;
-typedef isc_uint8_t dns_rpz_cidr_flags_t;
+/*
+ * A CIDR or radix tree node.
+ */
struct dns_rpz_cidr_node {
- dns_rpz_cidr_node_t *parent;
- dns_rpz_cidr_node_t *child[2];
- dns_rpz_cidr_key_t ip;
- dns_rpz_cidr_bits_t bits;
- dns_rpz_cidr_flags_t flags;
-#define DNS_RPZ_CIDR_FG_IP 0x01 /* has IP data or is parent of IP */
-#define DNS_RPZ_CIDR_FG_IP_DATA 0x02 /* has IP data */
-#define DNS_RPZ_CIDR_FG_NSIPv4 0x04 /* has or is parent of NSIPv4 data */
-#define DNS_RPZ_CIDR_FG_NSIPv6 0x08 /* has or is parent of NSIPv6 data */
-#define DNS_RPZ_CIDR_FG_NSIP_DATA 0x10 /* has NSIP data */
+ dns_rpz_cidr_node_t *parent;
+ dns_rpz_cidr_node_t *child[2];
+ dns_rpz_cidr_key_t ip;
+ dns_rpz_prefix_t prefix;
+ dns_rpz_pair_zbits_t pair;
+ dns_rpz_pair_zbits_t sum;
};
-struct dns_rpz_cidr {
- isc_mem_t *mctx;
- isc_boolean_t have_nsdname; /* zone has NSDNAME record */
- dns_rpz_cidr_node_t *root;
- dns_name_t ip_name; /* RPZ_IP_ZONE.origin. */
- dns_name_t nsip_name; /* RPZ_NSIP_ZONE.origin. */
- dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.origin */
+/*
+ * The data in a RBT node has two pairs of bits for policy zones.
+ * One pair is for the corresponding name of the node such as example.com
+ * and the other pair is for a wildcard child such as *.example.com.
+ */
+typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
+struct dns_rpz_nm_data {
+ dns_rpz_pair_zbits_t pair;
+ dns_rpz_pair_zbits_t wild;
};
-static isc_boolean_t have_rpz_zones = ISC_FALSE;
+#if 0
+/*
+ * Catch a name while debugging.
+ */
+static void
+catch_name(const dns_name_t *src_name, const char *tgt, const char *str) {
+ dns_fixedname_t tgt_namef;
+ dns_name_t *tgt_name;
+
+ dns_fixedname_init(&tgt_namef);
+ tgt_name = dns_fixedname_name(&tgt_namef);
+ dns_name_fromstring(tgt_name, tgt, DNS_NAME_DOWNCASE, NULL);
+ if (dns_name_equal(src_name, tgt_name)) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz hit failed: %s %s", str, tgt);
+ }
+}
+#endif
const char *
dns_rpz_type2str(dns_rpz_type_t type) {
case DNS_RPZ_TYPE_BAD:
break;
}
- FATAL_ERROR(__FILE__, __LINE__,
- "impossible rpz type %d", type);
+ FATAL_ERROR(__FILE__, __LINE__, "impossible rpz type %d", type);
return ("impossible");
}
return (str);
}
-/*
- * Free the radix tree of a response policy database.
- */
-void
-dns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) {
- dns_rpz_cidr_node_t *cur, *child, *parent;
- dns_rpz_cidr_t *cidr;
-
- REQUIRE(cidrp != NULL);
-
- cidr = *cidrp;
- if (cidr == NULL)
- return;
-
- cur = cidr->root;
- while (cur != NULL) {
- /* Depth first. */
- child = cur->child[0];
- if (child != NULL) {
- cur = child;
- continue;
- }
- child = cur->child[1];
- if (child != NULL) {
- cur = child;
- continue;
- }
-
- /* Delete this leaf and go up. */
- parent = cur->parent;
- if (parent == NULL)
- cidr->root = NULL;
- else
- parent->child[parent->child[1] == cur] = NULL;
- isc_mem_put(cidr->mctx, cur, sizeof(*cur));
- cur = parent;
+static int
+zbit_to_num(dns_rpz_zbits_t zbit) {
+ dns_rpz_num_t rpz_num;
+
+ INSIST(zbit != 0);
+ rpz_num = 0;
+#if DNS_RPZ_MAX_ZONES > 32
+ if ((zbit & 0xffffffff00000000L) != 0) {
+ zbit >>= 32;
+ rpz_num += 32;
}
-
- dns_name_free(&cidr->ip_name, cidr->mctx);
- dns_name_free(&cidr->nsip_name, cidr->mctx);
- dns_name_free(&cidr->nsdname_name, cidr->mctx);
- isc_mem_put(cidr->mctx, cidr, sizeof(*cidr));
- *cidrp = NULL;
-}
-
-/*
- * Forget a view's list of policy zones.
- */
-void
-dns_rpz_view_destroy(dns_view_t *view) {
- dns_rpz_zone_t *zone;
-
- REQUIRE(view != NULL);
-
- while (!ISC_LIST_EMPTY(view->rpz_zones)) {
- zone = ISC_LIST_HEAD(view->rpz_zones);
- ISC_LIST_UNLINK(view->rpz_zones, zone, link);
- if (dns_name_dynamic(&zone->origin))
- dns_name_free(&zone->origin, view->mctx);
- if (dns_name_dynamic(&zone->passthru))
- dns_name_free(&zone->passthru, view->mctx);
- if (dns_name_dynamic(&zone->nsdname))
- dns_name_free(&zone->nsdname, view->mctx);
- if (dns_name_dynamic(&zone->cname))
- dns_name_free(&zone->cname, view->mctx);
- isc_mem_put(view->mctx, zone, sizeof(*zone));
+#endif
+ if ((zbit & 0xffff0000) != 0) {
+ zbit >>= 16;
+ rpz_num += 16;
}
+ if ((zbit & 0xff00) != 0) {
+ zbit >>= 8;
+ rpz_num += 8;
+ }
+ if ((zbit & 0xf0) != 0) {
+ zbit >>= 4;
+ rpz_num += 4;
+ }
+ if ((zbit & 0xc) != 0) {
+ zbit >>= 2;
+ rpz_num += 2;
+ }
+ if ((zbit & 2) != 0)
+ ++rpz_num;
+ return (rpz_num);
}
-/*
- * Note that we have at least one response policy zone.
- * It would be better for something to tell the rbtdb code that the
- * zone is in at least one view's list of policy zones.
- */
-void
-dns_rpz_set_need(isc_boolean_t need) {
- have_rpz_zones = need;
-}
-
-isc_boolean_t
-dns_rpz_needed(void) {
- return (have_rpz_zones);
+static inline void
+make_pair(dns_rpz_pair_zbits_t *pair, dns_rpz_zbits_t zbits,
+ dns_rpz_type_t type)
+{
+ switch (type) {
+ case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_IP:
+ pair->d = zbits;
+ pair->ns = 0;
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ case DNS_RPZ_TYPE_NSIP:
+ pair->d = 0;
+ pair->ns = zbits;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
}
/*
- * Start a new radix tree for a response policy zone.
+ * Mark a node and all of its parents as having IP or NSIP data
*/
-isc_result_t
-dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
- dns_rpz_cidr_t **rbtdb_cidr)
-{
- isc_result_t result;
- dns_rpz_cidr_t *cidr;
-
- REQUIRE(rbtdb_cidr != NULL && *rbtdb_cidr == NULL);
-
- /*
- * Only if there is at least one response policy zone.
- */
- if (!have_rpz_zones)
- return (ISC_R_SUCCESS);
-
- cidr = isc_mem_get(mctx, sizeof(*cidr));
- if (cidr == NULL)
- return (ISC_R_NOMEMORY);
- memset(cidr, 0, sizeof(*cidr));
- cidr->mctx = mctx;
+static void
+set_sum_pair(dns_rpz_cidr_node_t *cnode) {
+ dns_rpz_cidr_node_t *child;
+ dns_rpz_pair_zbits_t sum;
- dns_name_init(&cidr->ip_name, NULL);
- result = dns_name_fromstring2(&cidr->ip_name, DNS_RPZ_IP_ZONE, origin,
- DNS_NAME_DOWNCASE, mctx);
- if (result != ISC_R_SUCCESS) {
- isc_mem_put(mctx, cidr, sizeof(*cidr));
- return (result);
- }
+ do {
+ sum = cnode->pair;
- dns_name_init(&cidr->nsip_name, NULL);
- result = dns_name_fromstring2(&cidr->nsip_name, DNS_RPZ_NSIP_ZONE,
- origin, DNS_NAME_DOWNCASE, mctx);
- if (result != ISC_R_SUCCESS) {
- dns_name_free(&cidr->ip_name, mctx);
- isc_mem_put(mctx, cidr, sizeof(*cidr));
- return (result);
- }
+ child = cnode->child[0];
+ if (child != NULL) {
+ sum.d |= child->sum.d;
+ sum.ns |= child->sum.ns;
+ }
- dns_name_init(&cidr->nsdname_name, NULL);
- result = dns_name_fromstring2(&cidr->nsdname_name, DNS_RPZ_NSDNAME_ZONE,
- origin, DNS_NAME_DOWNCASE, mctx);
- if (result != ISC_R_SUCCESS) {
- dns_name_free(&cidr->nsip_name, mctx);
- dns_name_free(&cidr->ip_name, mctx);
- isc_mem_put(mctx, cidr, sizeof(*cidr));
- return (result);
- }
+ child = cnode->child[1];
+ if (child != NULL) {
+ sum.d |= child->sum.d;
+ sum.ns |= child->sum.ns;
+ }
- *rbtdb_cidr = cidr;
- return (ISC_R_SUCCESS);
+ if (cnode->sum.d == sum.d &&
+ cnode->sum.ns == sum.ns)
+ break;
+ cnode->sum = sum;
+ cnode = cnode->parent;
+ } while (cnode != NULL);
}
-/*
- * 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;
- if (cidr->root != NULL &&
- (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv4) != 0)
- st->state |= DNS_RPZ_HAVE_NSIPv4;
- if (cidr->root != NULL &&
- (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0)
- st->state |= DNS_RPZ_HAVE_NSIPv6;
- if (cidr->have_nsdname)
- st->state |= DNS_RPZ_HAVE_NSDNAME;
-}
+static void
+fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
+ dns_rpz_zbits_t zbits;
-static inline dns_rpz_cidr_flags_t
-get_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix,
- dns_rpz_type_t rpz_type)
-{
- if (rpz_type == DNS_RPZ_TYPE_NSIP) {
- if (prefix >= 96 &&
- ip->w[0] == 0 && ip->w[1] == 0 &&
- ip->w[2] == ADDR_V4MAPPED)
- return (DNS_RPZ_CIDR_FG_NSIP_DATA |
- DNS_RPZ_CIDR_FG_NSIPv4);
- else
- return (DNS_RPZ_CIDR_FG_NSIP_DATA |
- DNS_RPZ_CIDR_FG_NSIPv6);
+ /*
+ * Get a mask covering all policy zones that are not subordinate to
+ * other policy zones containing triggers that require that the
+ * qname be resolved before they can be checked.
+ */
+ if (rpzs->p.qname_wait_recurse) {
+ zbits = 0;
} else {
- return (DNS_RPZ_CIDR_FG_IP | DNS_RPZ_CIDR_FG_IP_DATA);
+ zbits = (rpzs->have.ipv4 || rpzs->have.ipv6 ||
+ rpzs->have.nsdname ||
+ rpzs->have.nsipv4 || rpzs->have.nsipv6);
+ if (zbits == 0) {
+ zbits = DNS_RPZ_ALL_ZBITS;
+ } else {
+ zbits = DNS_RPZ_ZMASK(zbit_to_num(zbits));
+ }
}
+ rpzs->have.qname_skip_recurse = zbits;
+
+ rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
+ rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
}
-/*
- * Mark a node as having IP or NSIP data and all of its parents
- * as members of the IP or NSIP tree.
- */
static void
-set_node_flags(dns_rpz_cidr_node_t *node, dns_rpz_type_t rpz_type) {
- dns_rpz_cidr_flags_t flags;
+adj_trigger_cnt(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type,
+ const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ isc_boolean_t inc)
+{
+ dns_rpz_zone_t *rpz;
+ int *cnt;
+ dns_rpz_zbits_t *have;
- flags = get_flags(&node->ip, node->bits, rpz_type);
- node->flags |= flags;
- flags &= ~(DNS_RPZ_CIDR_FG_NSIP_DATA | DNS_RPZ_CIDR_FG_IP_DATA);
- for (;;) {
- node = node->parent;
- if (node == NULL)
- return;
- node->flags |= flags;
+ rpz = rpzs->zones[rpz_num];
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ cnt = &rpz->triggers.qname;
+ have = &rpzs->have.qname;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpz->triggers.ipv4;
+ have = &rpzs->have.ipv4;
+ } else {
+ cnt = &rpz->triggers.ipv6;
+ have = &rpzs->have.ipv6;
+ }
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ cnt = &rpz->triggers.nsdname;
+ have = &rpzs->have.nsdname;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpz->triggers.nsipv4;
+ have = &rpzs->have.nsipv4;
+ } else {
+ cnt = &rpz->triggers.nsipv6;
+ have = &rpzs->have.nsipv6;
+ }
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (inc) {
+ if (++*cnt == 1) {
+ *have |= DNS_RPZ_ZBIT(rpz_num);
+ fix_qname_skip_recurse(rpzs);
+ }
+ } else {
+ REQUIRE(*cnt > 0);
+ if (--*cnt == 0) {
+ *have &= ~DNS_RPZ_ZBIT(rpz_num);
+ fix_qname_skip_recurse(rpzs);
+ }
}
}
-/*
- * Make a radix tree node.
- */
static dns_rpz_cidr_node_t *
-new_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip,
- dns_rpz_cidr_bits_t bits, dns_rpz_cidr_flags_t flags)
+new_node(dns_rpz_zones_t *rpzs,
+ const dns_rpz_cidr_key_t *ip, dns_rpz_prefix_t prefix,
+ const dns_rpz_cidr_node_t *child)
{
- dns_rpz_cidr_node_t *node;
+ dns_rpz_cidr_node_t *new;
int i, words, wlen;
- node = isc_mem_get(cidr->mctx, sizeof(*node));
- if (node == NULL)
+ new = isc_mem_get(rpzs->mctx, sizeof(*new));
+ if (new == NULL)
return (NULL);
- memset(node, 0, sizeof(*node));
+ memset(new, 0, sizeof(*new));
- node->flags = flags & ~(DNS_RPZ_CIDR_FG_IP_DATA |
- DNS_RPZ_CIDR_FG_NSIP_DATA);
+ if (child != NULL)
+ new->sum = child->sum;
- node->bits = bits;
- words = bits / DNS_RPZ_CIDR_WORD_BITS;
- wlen = bits % DNS_RPZ_CIDR_WORD_BITS;
+ new->prefix = prefix;
+ words = prefix / DNS_RPZ_CIDR_WORD_BITS;
+ wlen = prefix % DNS_RPZ_CIDR_WORD_BITS;
i = 0;
while (i < words) {
- node->ip.w[i] = ip->w[i];
+ new->ip.w[i] = ip->w[i];
++i;
}
if (wlen != 0) {
- node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
+ new->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
++i;
}
while (i < DNS_RPZ_CIDR_WORDS)
- node->ip.w[i++] = 0;
+ new->ip.w[i++] = 0;
- return (node);
+ return (new);
}
static void
badname(int level, dns_name_t *name, const char *str1, const char *str2) {
- char printname[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "invalid rpz".
+ */
if (level < DNS_RPZ_DEBUG_QUIET
&& isc_log_wouldlog(dns_lctx, level)) {
- dns_name_format(name, printname, sizeof(printname));
+ dns_name_format(name, namebuf, sizeof(namebuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
DNS_LOGMODULE_RBTDB, level,
"invalid rpz IP address \"%s\"%s%s",
- printname, str1, str2);
+ namebuf, str1, str2);
}
}
/*
* Convert an IP address from radix tree binary (host byte order) to
- * to its canonical response policy domain name and its name in the
+ * to its canonical response policy domain name without the origin of the
* policy zone.
*/
static isc_result_t
-ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
- dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
- dns_name_t *canon_name, dns_name_t *search_name)
+ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ dns_name_t *base_name, dns_name_t *ip_name)
{
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
int w[DNS_RPZ_CIDR_WORDS*2];
char str[1+8+1+INET6_ADDRSTRLEN+1];
isc_buffer_t buffer;
- dns_name_t *name;
isc_result_t result;
isc_boolean_t zeros;
int i, n, len;
- if (tgt_prefix > 96 &&
- tgt_ip->w[0] == 0 &&
- tgt_ip->w[1] == 0 &&
- tgt_ip->w[2] == ADDR_V4MAPPED) {
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
len = snprintf(str, sizeof(str), "%d.%d.%d.%d.%d",
tgt_prefix - 96,
tgt_ip->w[3] & 0xff,
(tgt_ip->w[3]>>8) & 0xff,
(tgt_ip->w[3]>>16) & 0xff,
(tgt_ip->w[3]>>24) & 0xff);
- if (len == -1 || len > (int)sizeof(str))
+ if (len < 0 || len > (int)sizeof(str))
return (ISC_R_FAILURE);
} else {
for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
}
}
- if (canon_name != NULL) {
- isc__buffer_init(&buffer, str, sizeof(str));
- isc__buffer_add(&buffer, len);
- result = dns_name_fromtext(canon_name, &buffer,
- dns_rootname, 0, NULL);
- if (result != ISC_R_SUCCESS)
- return (result);
- }
- if (search_name != NULL) {
- isc__buffer_init(&buffer, str, sizeof(str));
- isc__buffer_add(&buffer, len);
- if (type == DNS_RPZ_TYPE_NSIP)
- name = &cidr->nsip_name;
- else
- name = &cidr->ip_name;
- result = dns_name_fromtext(search_name, &buffer, name, 0, NULL);
- if (result != ISC_R_SUCCESS)
- return (result);
- }
- return (ISC_R_SUCCESS);
+ isc__buffer_init(&buffer, str, sizeof(str));
+ isc__buffer_add(&buffer, len);
+ result = dns_name_fromtext(ip_name, &buffer, base_name, 0, NULL);
+ return (result);
}
/*
- * Decide which kind of IP address response policy zone a name is in.
+ * Determine the type a of a name in a response policy zone.
*/
static dns_rpz_type_t
-set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+type_from_name(dns_rpz_zone_t *rpz, dns_name_t *name) {
- if (dns_name_issubdomain(name, &cidr->ip_name))
+ if (dns_name_issubdomain(name, &rpz->ip))
return (DNS_RPZ_TYPE_IP);
/*
* until consistency problems are resolved.
*/
#ifdef ENABLE_RPZ_NSIP
- if (dns_name_issubdomain(name, &cidr->nsip_name))
+ if (dns_name_issubdomain(name, &rpz->nsip))
return (DNS_RPZ_TYPE_NSIP);
#endif
#ifdef ENABLE_RPZ_NSDNAME
- if (dns_name_issubdomain(name, &cidr->nsdname_name))
+ if (dns_name_issubdomain(name, &rpz->nsdname))
return (DNS_RPZ_TYPE_NSDNAME);
#endif
/*
* Convert an IP address from canonical response policy domain name form
- * to radix tree binary (host byte order).
+ * to radix tree binary (host byte order) for adding or deleting IP or NSIP
+ * data.
*/
static isc_result_t
-name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
- dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip,
- dns_rpz_cidr_bits_t *tgt_prefix)
+name2ipkey(int log_level,
+ const dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name,
+ dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t *tgt_prefix,
+ dns_rpz_pair_zbits_t *new_pair)
{
- isc_result_t result;
- dns_fixedname_t fname;
- dns_name_t *ipname;
- char ipstr[DNS_NAME_FORMATSIZE];
+ dns_rpz_zone_t *rpz;
+ char ip_str[DNS_NAME_FORMATSIZE];
+ dns_offsets_t ip_name_offsets;
+ dns_fixedname_t ip_name2f;
+ dns_name_t ip_name, *ip_name2;
const char *prefix_str, *cp, *end;
char *cp2;
int ip_labels;
- dns_rpz_cidr_bits_t bits;
- unsigned long prefix, l;
+ dns_rpz_prefix_t prefix;
+ unsigned long prefix_num, l;
+ isc_result_t result;
int i;
- /*
- * Need at least enough labels for the shortest name,
- * :: or 128.*.RPZ_x_ZONE.rpz.LOCALHOST.
- */
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ make_pair(new_pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+
ip_labels = dns_name_countlabels(src_name);
- ip_labels -= dns_name_countlabels(&cidr->ip_name);
- ip_labels--;
- if (ip_labels < 1) {
- badname(level, src_name, "; too short", "");
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ ip_labels -= dns_name_countlabels(&rpz->origin);
+ else
+ ip_labels -= dns_name_countlabels(&rpz->nsdname);
+ if (ip_labels < 2) {
+ badname(log_level, src_name, "; too short", "");
return (ISC_R_FAILURE);
}
+ dns_name_init(&ip_name, ip_name_offsets);
+ dns_name_getlabelsequence(src_name, 0, ip_labels, &ip_name);
/*
* Get text for the IP address
*/
- dns_fixedname_init(&fname);
- ipname = dns_fixedname_name(&fname);
- dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name),
- ipname, NULL);
- dns_name_format(ipname, ipstr, sizeof(ipstr));
- end = &ipstr[strlen(ipstr)+1];
- prefix_str = ipstr;
-
- prefix = strtoul(prefix_str, &cp2, 10);
+ dns_name_format(&ip_name, ip_str, sizeof(ip_str));
+ end = &ip_str[strlen(ip_str)+1];
+ prefix_str = ip_str;
+
+ prefix_num = strtoul(prefix_str, &cp2, 10);
if (*cp2 != '.') {
- badname(level, src_name,
+ badname(log_level, src_name,
"; invalid leading prefix length", "");
return (ISC_R_FAILURE);
}
*cp2 = '\0';
- if (prefix < 1U || prefix > 128U) {
- badname(level, src_name,
+ if (prefix_num < 1U || prefix_num > 128U) {
+ badname(log_level, src_name,
"; invalid prefix length of ", prefix_str);
return (ISC_R_FAILURE);
}
cp = cp2+1;
- if (ip_labels == 4 && !strchr(cp, 'z')) {
+ if (--ip_labels == 4 && !strchr(cp, 'z')) {
/*
* Convert an IPv4 address
* from the form "prefix.w.z.y.x"
*/
- if (prefix > 32U) {
- badname(level, src_name,
+ if (prefix_num > 32U) {
+ badname(log_level, src_name,
"; invalid IPv4 prefix length of ", prefix_str);
return (ISC_R_FAILURE);
}
- prefix += 96;
- *tgt_prefix = (dns_rpz_cidr_bits_t)prefix;
+ prefix_num += 96;
+ *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
tgt_ip->w[0] = 0;
tgt_ip->w[1] = 0;
tgt_ip->w[2] = ADDR_V4MAPPED;
if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) {
if (*cp2 == '.')
*cp2 = '\0';
- badname(level, src_name,
+ badname(log_level, src_name,
"; invalid IPv4 octet ", cp);
return (ISC_R_FAILURE);
}
/*
* Convert a text IPv6 address.
*/
- *tgt_prefix = (dns_rpz_cidr_bits_t)prefix;
+ *tgt_prefix = (dns_rpz_prefix_t)prefix_num;
for (i = 0;
ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2;
ip_labels--) {
(*cp2 != '.' && *cp2 != '\0')) {
if (*cp2 == '.')
*cp2 = '\0';
- badname(level, src_name,
+ badname(log_level, src_name,
"; invalid IPv6 word ", cp);
return (ISC_R_FAILURE);
}
}
}
if (cp != end) {
- badname(level, src_name, "", "");
+ badname(log_level, src_name, "", "");
return (ISC_R_FAILURE);
}
/*
* Check for 1s after the prefix length.
*/
- bits = (dns_rpz_cidr_bits_t)prefix;
- while (bits < DNS_RPZ_CIDR_KEY_BITS) {
+ prefix = (dns_rpz_prefix_t)prefix_num;
+ while (prefix < DNS_RPZ_CIDR_KEY_BITS) {
dns_rpz_cidr_word_t aword;
- i = bits % DNS_RPZ_CIDR_WORD_BITS;
- aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS];
+ i = prefix % DNS_RPZ_CIDR_WORD_BITS;
+ aword = tgt_ip->w[prefix / DNS_RPZ_CIDR_WORD_BITS];
if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) {
- badname(level, src_name,
+ badname(log_level, src_name,
"; too small prefix length of ", prefix_str);
return (ISC_R_FAILURE);
}
- bits -= i;
- bits += DNS_RPZ_CIDR_WORD_BITS;
+ prefix -= i;
+ prefix += DNS_RPZ_CIDR_WORD_BITS;
}
/*
- * Convert the address back to a canonical policy domain name
- * to ensure that it is in canonical form.
+ * Convert the address back to a canonical domain name
+ * to ensure that the original name is in canonical form.
*/
- result = ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t) prefix,
- type, NULL, ipname);
- if (result != ISC_R_SUCCESS || !dns_name_equal(src_name, ipname)) {
- badname(level, src_name, "; not canonical", "");
+ dns_fixedname_init(&ip_name2f);
+ ip_name2 = dns_fixedname_name(&ip_name2f);
+ result = ip2name(tgt_ip, (dns_rpz_prefix_t)prefix_num, NULL, ip_name2);
+ if (result != ISC_R_SUCCESS || !dns_name_equal(&ip_name, ip_name2)) {
+ badname(log_level, src_name, "; not canonical", "");
return (ISC_R_FAILURE);
}
}
/*
- * Find first differing bit.
+ * Get trigger name and data bits for adding or deleting summary NSDNAME
+ * or QNAME data.
*/
-static int
-ffbit(dns_rpz_cidr_word_t w) {
+static void
+name2data(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, const dns_name_t *src_name,
+ dns_name_t *trig_name, dns_rpz_nm_data_t *new_data)
+{
+ static dns_rpz_pair_zbits_t zero = {0, 0};
+ dns_rpz_zone_t *rpz;
+ dns_offsets_t tmp_name_offsets;
+ dns_name_t tmp_name;
+ unsigned int prefix_len, n;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ /*
+ * Handle wildcards by putting only the parent into the
+ * summary RBT. The summary database only causes a check of the
+ * real policy zone where wildcards will be handled.
+ */
+ if (dns_name_iswildcard(src_name)) {
+ prefix_len = 1;
+ new_data->pair = zero;
+ make_pair(&new_data->wild, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+ } else {
+ prefix_len = 0;
+ make_pair(&new_data->pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+ new_data->wild = zero;
+ }
+
+ dns_name_init(&tmp_name, tmp_name_offsets);
+ n = dns_name_countlabels(src_name);
+ n -= prefix_len;
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ n -= dns_name_countlabels(&rpz->origin);
+ else
+ n -= dns_name_countlabels(&rpz->nsdname);
+ dns_name_getlabelsequence(src_name, prefix_len, n, &tmp_name);
+ (void)dns_name_concatenate(&tmp_name, dns_rootname, trig_name, NULL);
+}
+
+/*
+ * Find the first differing bit in a key (IP address) word.
+ */
+static inline int
+ffs_keybit(dns_rpz_cidr_word_t w) {
int bit;
bit = DNS_RPZ_CIDR_WORD_BITS-1;
}
/*
- * Find the first differing bit in two keys.
+ * Find the first differing bit in two keys (IP addresses).
*/
static int
-diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1,
- const dns_rpz_cidr_key_t *key2, dns_rpz_cidr_bits_t bits2)
+diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_prefix_t prefix1,
+ const dns_rpz_cidr_key_t *key2, dns_rpz_prefix_t prefix2)
{
dns_rpz_cidr_word_t delta;
- dns_rpz_cidr_bits_t maxbit, bit;
+ dns_rpz_prefix_t maxbit, bit;
int i;
- maxbit = ISC_MIN(bits1, bits2);
+ maxbit = ISC_MIN(prefix1, prefix2);
/*
* find the first differing words
i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
delta = key1->w[i] ^ key2->w[i];
if (delta != 0) {
- bit += ffbit(delta);
+ bit += ffs_keybit(delta);
break;
}
}
return (ISC_MIN(bit, maxbit));
}
+/*
+ * Given a hit while searching the radix trees,
+ * clear all bits for higher numbered zones.
+ */
+static inline dns_rpz_zbits_t
+trim_zbits(dns_rpz_zbits_t zbits, dns_rpz_zbits_t found) {
+ dns_rpz_zbits_t x;
+
+ /*
+ * Isolate the first or smallest numbered hit bit.
+ * Make a mask of that bit and all smaller numbered bits.
+ */
+ x = zbits & found;
+ x &= -x;
+ x = (x << 1) - 1;
+ return (zbits &= x);
+}
+
/*
* 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,
- * ISC_R_NOMEMORY
+ * Return ISC_R_SUCCESS, DNS_R_PARTIALMATCH, ISC_R_NOTFOUND,
+ * and *found=longest match node
+ * or with create==ISC_TRUE, ISC_R_EXISTS or ISC_R_NOMEMORY
*/
static isc_result_t
-search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
- dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
- isc_boolean_t create,
- dns_rpz_cidr_node_t **found) /* NULL or longest match node */
+search(dns_rpz_zones_t *rpzs,
+ const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
+ const dns_rpz_pair_zbits_t *tgt_pair, isc_boolean_t create,
+ dns_rpz_cidr_node_t **found)
{
dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling;
+ dns_rpz_pair_zbits_t pair;
int cur_num, child_num;
- dns_rpz_cidr_bits_t dbit;
- dns_rpz_cidr_flags_t flags, data_flag;
+ dns_rpz_prefix_t dbit;
isc_result_t find_result;
- flags = get_flags(tgt_ip, tgt_prefix, type);
- data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
- DNS_RPZ_CIDR_FG_NSIP_DATA);
-
+ pair = *tgt_pair;
find_result = ISC_R_NOTFOUND;
- if (found != NULL)
- *found = NULL;
- cur = cidr->root;
+ *found = NULL;
+ cur = rpzs->cidr;
parent = NULL;
cur_num = 0;
for (;;) {
if (cur == NULL) {
/*
- * No child so we cannot go down. Fail or
- * add the target as a child of the current parent.
+ * No child so we cannot go down.
+ * Quit with whatever we already found
+ * or add the target as a child of the current parent.
*/
if (!create)
return (find_result);
- child = new_node(cidr, tgt_ip, tgt_prefix, 0);
+ child = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
if (child == NULL)
return (ISC_R_NOMEMORY);
if (parent == NULL)
- cidr->root = child;
+ rpzs->cidr = child;
else
parent->child[cur_num] = child;
child->parent = parent;
- set_node_flags(child, type);
- if (found != NULL)
- *found = cur;
+ child->pair.d |= tgt_pair->d;
+ child->pair.ns |= tgt_pair->ns;
+ set_sum_pair(child);
+ *found = cur;
return (ISC_R_SUCCESS);
}
- /*
- * Pretend a node not in the correct tree does not exist
- * if we are not adding to the tree,
- * If we are adding, then continue down to eventually
- * add a node and mark/put this node in the correct tree.
- */
- if ((cur->flags & flags) == 0 && !create)
- return (find_result);
+ if ((cur->sum.d & pair.d) == 0 &&
+ (cur->sum.ns & pair.ns) == 0) {
+ /*
+ * This node has no relevant data
+ * and is in none of the target trees.
+ * Pretend it does not exist if we are not adding.
+ *
+ * If we are adding, continue down to eventually add
+ * a node and mark/put this node in the correct tree.
+ */
+ if (!create)
+ return (find_result);
+ }
- dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->bits);
+ dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->prefix);
/*
- * dbit <= tgt_prefix and dbit <= cur->bits always.
+ * dbit <= tgt_prefix and dbit <= cur->prefix always.
* We are finished searching if we matched all of the target.
*/
if (dbit == tgt_prefix) {
- if (tgt_prefix == cur->bits) {
+ if (tgt_prefix == cur->prefix) {
/*
- * The current node matches the target exactly.
- * It is the answer if it has data.
+ * The node's key matches the target exactly.
*/
- if ((cur->flags & data_flag) != 0) {
- if (create)
- return (ISC_R_EXISTS);
- if (found != NULL)
- *found = cur;
- return (ISC_R_SUCCESS);
+ if ((cur->pair.d & pair.d) != 0 ||
+ (cur->pair.ns & pair.ns) != 0) {
+ /*
+ * It is the answer if it has data.
+ */
+ *found = cur;
+ if (create) {
+ find_result = ISC_R_EXISTS;
+ } else {
+ find_result = ISC_R_SUCCESS;
+ }
} else if (create) {
/*
- * The node had no data but does now.
+ * The node lacked relevant data,
+ * but will have it now.
*/
- set_node_flags(cur, type);
- if (found != NULL)
- *found = cur;
- return (ISC_R_SUCCESS);
+ cur->pair.d |= tgt_pair->d;
+ cur->pair.ns |= tgt_pair->ns;
+ set_sum_pair(cur);
+ *found = cur;
+ find_result = ISC_R_SUCCESS;
}
return (find_result);
}
/*
- * We know tgt_prefix < cur_bits which means that
+ * We know tgt_prefix < cur->prefix which means that
* the target is shorter than the current node.
* Add the target as the current node's parent.
*/
if (!create)
return (find_result);
- new_parent = new_node(cidr, tgt_ip, tgt_prefix,
- cur->flags);
+ new_parent = new_node(rpzs, tgt_ip, tgt_prefix, cur);
if (new_parent == NULL)
return (ISC_R_NOMEMORY);
new_parent->parent = parent;
if (parent == NULL)
- cidr->root = new_parent;
+ rpzs->cidr = new_parent;
else
parent->child[cur_num] = new_parent;
child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1);
new_parent->child[child_num] = cur;
cur->parent = new_parent;
- set_node_flags(new_parent, type);
- if (found != NULL)
- *found = new_parent;
+ new_parent->pair = *tgt_pair;
+ set_sum_pair(new_parent);
+ *found = new_parent;
return (ISC_R_SUCCESS);
}
- if (dbit == cur->bits) {
- /*
- * We have a partial match by matching of all of the
- * current node but only part of the target.
- * Try to go down.
- */
- if ((cur->flags & data_flag) != 0) {
+ if (dbit == cur->prefix) {
+ if ((cur->pair.d & pair.d) != 0 ||
+ (cur->pair.ns & pair.ns) != 0) {
+ /*
+ * We have a partial match between of all of the
+ * current node but only part of the target.
+ * Continue searching for other hits in the
+ * same or lower numbered trees.
+ */
find_result = DNS_R_PARTIALMATCH;
- if (found != NULL)
- *found = cur;
+ *found = cur;
+ pair.d = trim_zbits(pair.d, cur->pair.d);
+ pair.ns = trim_zbits(pair.ns, cur->pair.ns);
}
-
parent = cur;
cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
cur = cur->child[cur_num];
/*
- * dbit < tgt_prefix and dbit < cur->bits,
+ * dbit < tgt_prefix and dbit < cur->prefix,
* so we failed to match both the target and the current node.
* Insert a fork of a parent above the current node and
* add the target as a sibling of the current node
if (!create)
return (find_result);
- sibling = new_node(cidr, tgt_ip, tgt_prefix, 0);
+ sibling = new_node(rpzs, tgt_ip, tgt_prefix, NULL);
if (sibling == NULL)
return (ISC_R_NOMEMORY);
- new_parent = new_node(cidr, tgt_ip, dbit, cur->flags);
+ new_parent = new_node(rpzs, tgt_ip, dbit, cur);
if (new_parent == NULL) {
- isc_mem_put(cidr->mctx, sibling, sizeof(*sibling));
+ isc_mem_put(rpzs->mctx, sibling, sizeof(*sibling));
return (ISC_R_NOMEMORY);
}
new_parent->parent = parent;
if (parent == NULL)
- cidr->root = new_parent;
+ rpzs->cidr = new_parent;
else
parent->child[cur_num] = new_parent;
child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
new_parent->child[1-child_num] = cur;
cur->parent = new_parent;
sibling->parent = new_parent;
- set_node_flags(sibling, type);
- if (found != NULL)
- *found = sibling;
+ sibling->pair = *tgt_pair;
+ set_sum_pair(sibling);
+ *found = sibling;
return (ISC_R_SUCCESS);
}
}
/*
- * Add an IP address to the radix tree of a response policy database.
- * The tree write lock must be held by the caller.
+ * Add an IP address to the radix tree.
*/
-void
-dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
- isc_result_t result;
+static isc_result_t
+add_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
dns_rpz_cidr_key_t tgt_ip;
- dns_rpz_cidr_bits_t tgt_prefix;
- dns_rpz_type_t type;
-
- if (cidr == NULL)
- return;
+ dns_rpz_prefix_t tgt_prefix;
+ dns_rpz_pair_zbits_t pair;
+ dns_rpz_cidr_node_t *found;
+ isc_result_t result;
+ result = name2ipkey(DNS_RPZ_ERROR_LEVEL, rpzs, rpz_num, rpz_type,
+ src_name, &tgt_ip, &tgt_prefix, &pair);
/*
- * No worries if the new name is not an IP address.
+ * Log complaints about bad owner names but let the zone load.
*/
- type = set_type(cidr, name);
- switch (type) {
- case DNS_RPZ_TYPE_IP:
- case DNS_RPZ_TYPE_NSIP:
- break;
- case DNS_RPZ_TYPE_NSDNAME:
- cidr->have_nsdname = ISC_TRUE;
- return;
- case DNS_RPZ_TYPE_QNAME:
- case DNS_RPZ_TYPE_BAD:
- return;
- }
- result = name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name,
- type, &tgt_ip, &tgt_prefix);
if (result != ISC_R_SUCCESS)
- return;
+ return (ISC_R_SUCCESS);
- result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_TRUE, NULL);
- if (result == ISC_R_EXISTS &&
- isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL))
- {
- char printname[DNS_NAME_FORMATSIZE];
+ result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_TRUE, &found);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
- dns_name_format(name, printname, sizeof(printname));
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
- "rpz add failed; \"%s\" is a duplicate name",
- printname);
+ "rpz add_cidr(%s) failed: %s",
+ namebuf, isc_result_totext(result));
+ return (result);
}
+
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_TRUE);
+ return (result);
}
-/*
- * Delete an IP address from the radix tree of a response policy database.
- * The tree write lock must be held by the caller.
- */
-void
-dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+static isc_result_t
+add_nm(dns_rpz_zones_t *rpzs, dns_name_t *trig_name,
+ const dns_rpz_nm_data_t *new_data)
+{
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data;
isc_result_t result;
- dns_rpz_cidr_key_t tgt_ip;
- dns_rpz_cidr_bits_t tgt_prefix;
- dns_rpz_type_t type;
- dns_rpz_cidr_node_t *tgt = NULL, *parent, *child;
- dns_rpz_cidr_flags_t flags, data_flag;
- if (cidr == NULL)
- return;
+ nmnode = NULL;
+ result = dns_rbt_addnode(rpzs->rbt, trig_name, &nmnode);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case ISC_R_EXISTS:
+ nm_data = nmnode->data;
+ if (nm_data == NULL) {
+ nm_data = isc_mem_get(rpzs->mctx, sizeof(*nm_data));
+ if (nm_data == NULL)
+ return (ISC_R_NOMEMORY);
+ *nm_data = *new_data;
+ nmnode->data = nm_data;
+ return (ISC_R_SUCCESS);
+ }
+ break;
+ default:
+ return (result);
+ }
/*
- * Decide which kind of policy zone IP address it is, if either
- * and then find its node.
+ * Do not count bits that are already present
*/
- type = set_type(cidr, name);
- switch (type) {
- case DNS_RPZ_TYPE_IP:
- case DNS_RPZ_TYPE_NSIP:
- break;
- case DNS_RPZ_TYPE_NSDNAME:
+ if ((nm_data->pair.d & new_data->pair.d) != 0 ||
+ (nm_data->pair.ns & new_data->pair.ns) != 0 ||
+ (nm_data->wild.d & new_data->wild.d) != 0 ||
+ (nm_data->wild.ns & new_data->wild.ns) != 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
/*
- * We cannot easily count nsdnames because
- * internal rbt nodes get deleted.
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
*/
- return;
- case DNS_RPZ_TYPE_QNAME:
- case DNS_RPZ_TYPE_BAD:
- return;
+ dns_name_format(trig_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz add_nm(%s): bits already set", namebuf);
+ return (ISC_R_EXISTS);
}
- /*
- * Do not get excited about the deletion of interior rbt nodes.
- */
- result = name2ipkey(cidr, DNS_RPZ_DEBUG_QUIET, name,
- type, &tgt_ip, &tgt_prefix);
- if (result != ISC_R_SUCCESS)
- return;
-
- result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_FALSE, &tgt);
+ nm_data->pair.d |= new_data->pair.d;
+ nm_data->pair.ns |= new_data->pair.ns;
+ nm_data->wild.d |= new_data->wild.d;
+ nm_data->wild.ns |= new_data->wild.ns;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ dns_rpz_nm_data_t new_data;
+ dns_fixedname_t trig_namef;
+ dns_name_t *trig_name;
+ isc_result_t result;
+
+ dns_fixedname_init(&trig_namef);
+ trig_name = dns_fixedname_name(&trig_namef);
+ name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &new_data);
+
+ result = add_nm(rpzs, trig_name, &new_data);
+ if (result == ISC_R_SUCCESS)
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_TRUE);
+ return (result);
+}
+
+/*
+ * Callback to free the data for a node in the summary RBT database.
+ */
+static void
+rpz_node_deleter(void *nm_data, void *mctx) {
+ isc_mem_put(mctx, nm_data, sizeof(dns_rpz_nm_data_t));
+}
+
+/*
+ * Get ready for a new set of policy zones.
+ */
+isc_result_t
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx) {
+ dns_rpz_zones_t *new;
+ isc_result_t result;
+
+ REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+
+ *rpzsp = NULL;
+
+ new = isc_mem_get(mctx, sizeof(*new));
+ if (new == NULL)
+ return (ISC_R_NOMEMORY);
+ memset(new, 0, sizeof(*new));
+
+ result = isc_mutex_init(&new->search_lock);
if (result != ISC_R_SUCCESS) {
- badname(DNS_RPZ_ERROR_LEVEL, name, "; missing rpz node", "");
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = isc_mutex_init(&new->maint_lock);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = isc_refcount_init(&new->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&new->maint_lock);
+ DESTROYLOCK(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &new->rbt);
+ if (result != ISC_R_SUCCESS) {
+ isc_refcount_decrement(&new->refs, NULL);
+ isc_refcount_destroy(&new->refs);
+ DESTROYLOCK(&new->maint_lock);
+ DESTROYLOCK(&new->search_lock);
+ isc_mem_put(mctx, new, sizeof(*new));
+ return (result);
+ }
+
+ isc_mem_attach(mctx, &new->mctx);
+
+ *rpzsp = new;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Free the radix tree of a response policy database.
+ */
+static void
+cidr_free(dns_rpz_zones_t *rpzs) {
+ dns_rpz_cidr_node_t *cur, *child, *parent;
+
+ cur = rpzs->cidr;
+ while (cur != NULL) {
+ /* Depth first. */
+ child = cur->child[0];
+ if (child != NULL) {
+ cur = child;
+ continue;
+ }
+ child = cur->child[1];
+ if (child != NULL) {
+ cur = child;
+ continue;
+ }
+
+ /* Delete this leaf and go up. */
+ parent = cur->parent;
+ if (parent == NULL)
+ rpzs->cidr = NULL;
+ else
+ parent->child[parent->child[1] == cur] = NULL;
+ isc_mem_put(rpzs->mctx, cur, sizeof(*cur));
+ cur = parent;
+ }
+}
+
+/*
+ * Discard a response policy zone blob
+ * before discarding the overall rpz structure.
+ */
+static void
+rpz_detach(dns_rpz_zone_t **rpzp, dns_rpz_zones_t *rpzs) {
+ dns_rpz_zone_t *rpz;
+ unsigned int refs;
+
+ rpz = *rpzp;
+ *rpzp = NULL;
+ isc_refcount_decrement(&rpz->refs, &refs);
+ if (refs != 0)
return;
+ isc_refcount_destroy(&rpz->refs);
+
+ if (dns_name_dynamic(&rpz->origin))
+ dns_name_free(&rpz->origin, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->ip))
+ dns_name_free(&rpz->ip, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->nsdname))
+ dns_name_free(&rpz->nsdname, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->nsip))
+ dns_name_free(&rpz->nsip, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->passthru))
+ dns_name_free(&rpz->passthru, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->cname))
+ dns_name_free(&rpz->cname, rpzs->mctx);
+
+ isc_mem_put(rpzs->mctx, rpz, sizeof(*rpz));
+}
+
+void
+dns_rpz_attach_rpzs(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **rpzsp) {
+ REQUIRE(rpzsp != NULL && *rpzsp == NULL);
+ isc_refcount_increment(&rpzs->refs, NULL);
+ *rpzsp = rpzs;
+}
+
+/*
+ * Forget a view's policy zones.
+ */
+void
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) {
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_num_t rpz_num;
+ unsigned int refs;
+
+ REQUIRE(rpzsp != NULL);
+ rpzs = *rpzsp;
+ REQUIRE(rpzs != NULL);
+
+ *rpzsp = NULL;
+ isc_refcount_decrement(&rpzs->refs, &refs);
+
+ /*
+ * Forget the last of view's rpz machinery after the last reference.
+ */
+ if (refs == 0) {
+ for (rpz_num = 0; rpz_num < DNS_RPZ_MAX_ZONES; ++rpz_num) {
+ rpz = rpzs->zones[rpz_num];
+ rpzs->zones[rpz_num] = NULL;
+ if (rpz != NULL)
+ rpz_detach(&rpz, rpzs);
+ }
+
+ cidr_free(rpzs);
+ dns_rbt_destroy(&rpzs->rbt);
+ DESTROYLOCK(&rpzs->maint_lock);
+ DESTROYLOCK(&rpzs->search_lock);
+ isc_refcount_destroy(&rpzs->refs);
+ isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
}
+}
+
+/*
+ * Create empty summary database to load one zone.
+ * The RBTDB write tree lock must be held.
+ */
+isc_result_t
+dns_rpz_beginload(dns_rpz_zones_t **load_rpzsp,
+ dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num)
+{
+ dns_rpz_zones_t *load_rpzs;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_zbits_t tgt;
+ isc_result_t result;
+
+ REQUIRE(rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
/*
- * Mark the node and its parents to reflect the deleted IP address.
+ * When reloading a zone, there are usually records among the summary
+ * data for the zone. Some of those records might be deleted by the
+ * reloaded zone data. To deal with that case:
+ * reload the new zone data into a new blank summary database
+ * if the reload fails, discard the new summary database
+ * if the new zone data is acceptable, copy the records for the
+ * other zones into the new summary database and replace the
+ * old summary database with the new.
+ *
+ * At the first attempt to load a zone, there is no summary data
+ * for the zone and so no records that need to be deleted.
+ * This is also the most common case of policy zone loading.
+ * Most policy zone maintenance should be by incremental changes
+ * and so by the addition and deletion of individual records.
+ * Detect that case and load records the first time into the
+ * operational summary database
*/
- flags = get_flags(&tgt_ip, tgt_prefix, type);
- data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
- DNS_RPZ_CIDR_FG_NSIP_DATA);
- tgt->flags &= ~data_flag;
- for (parent = tgt; parent != NULL; parent = parent->parent) {
- if ((parent->flags & data_flag) != 0 ||
- (parent->child[0] != NULL &&
- (parent->child[0]->flags & flags) != 0) ||
- (parent->child[1] != NULL &&
- (parent->child[1]->flags & flags) != 0))
- break;
- parent->flags &= ~flags;
+ tgt = DNS_RPZ_ZBIT(rpz_num);
+ LOCK(&rpzs->maint_lock);
+ LOCK(&rpzs->search_lock);
+ if ((rpzs->load_begun & tgt) == 0) {
+ rpzs->load_begun |= tgt;
+ dns_rpz_attach_rpzs(rpzs, load_rpzsp);
+ UNLOCK(&rpzs->search_lock);
+ UNLOCK(&rpzs->maint_lock);
+
+ } else {
+ UNLOCK(&rpzs->search_lock);
+ UNLOCK(&rpzs->maint_lock);
+
+ result = dns_rpz_new_zones(load_rpzsp, rpzs->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ load_rpzs = *load_rpzsp;
+ load_rpzs->p.num_zones = rpzs->p.num_zones;
+ load_rpzs->zones[rpz_num] = rpz;
+ isc_refcount_increment(&rpz->refs, NULL);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_triggers_t *totals) {
+ dns_rpz_num_t rpz_num;
+ const dns_rpz_zone_t *rpz;
+ dns_rpz_zbits_t zbit;
+
+# define SET_TRIG(type) \
+ if (rpz == NULL || rpz->triggers.type == 0) { \
+ rpzs->have.type &= ~zbit; \
+ } else { \
+ totals->type += rpz->triggers.type; \
+ rpzs->have.type |= zbit; \
+ }
+
+ memset(totals, 0, sizeof(*totals));
+ for (rpz_num = 0; rpz_num < rpzs->p.num_zones; ++rpz_num) {
+ rpz = rpzs->zones[rpz_num];
+ zbit = DNS_RPZ_ZBIT(rpz_num);
+ SET_TRIG(nsdname);
+ SET_TRIG(qname);
+ SET_TRIG(ipv4);
+ SET_TRIG(ipv6);
+ SET_TRIG(nsipv4);
+ SET_TRIG(nsipv6);
+ }
+
+ fix_qname_skip_recurse(rpzs);
+
+# undef SET_TRIG
+}
+
+static void
+load_unlock(dns_rpz_zones_t *rpzs, dns_rpz_zones_t **load_rpzsp) {
+ UNLOCK(&rpzs->maint_lock);
+ UNLOCK(&(*load_rpzsp)->search_lock);
+ UNLOCK(&(*load_rpzsp)->maint_lock);
+ dns_rpz_detach_rpzs(load_rpzsp);
+}
+
+/*
+ * Finish loading one zone.
+ * The RBTDB write tree lock must be held.
+ */
+isc_result_t
+dns_rpz_ready(dns_rpz_zones_t *rpzs,
+ dns_rpz_zones_t **load_rpzsp, dns_rpz_num_t rpz_num)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_rpz_zones_t *load_rpzs;
+ const dns_rpz_cidr_node_t *cnode, *next_cnode, *parent_cnode;
+ dns_rpz_cidr_node_t *found;
+ dns_rpz_pair_zbits_t load_pair, new_pair;
+ dns_rbt_t *rbt;
+ dns_rbtnodechain_t chain;
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data, new_data;
+ dns_rpz_triggers_t old_totals, new_totals;
+ dns_fixedname_t labelf, originf, namef;
+ dns_name_t *label, *origin, *name;
+ isc_result_t result;
+
+ INSIST(rpzs != NULL);
+ LOCK(&rpzs->maint_lock);
+ load_rpzs = *load_rpzsp;
+ INSIST(load_rpzs != NULL);
+
+ if (load_rpzs == rpzs) {
+ /*
+ * This is a successful initial zone loading,
+ * perhaps for a new instance of a view.
+ */
+ fix_triggers(rpzs, &new_totals);
+ UNLOCK(&rpzs->maint_lock);
+ dns_rpz_detach_rpzs(load_rpzsp);
+
+ if (rpz_num == rpzs->p.num_zones-1)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+ "loaded policy %d zones with"
+ " %d qname %d nsdname"
+ " % d IP %d NSIP entries",
+ rpzs->p.num_zones,
+ new_totals.qname,
+ new_totals.nsdname,
+ new_totals.ipv4 + new_totals.ipv6,
+ new_totals.nsipv4 +
+ new_totals.nsipv6);
+ return (ISC_R_SUCCESS);
+ }
+
+ LOCK(&load_rpzs->maint_lock);
+ LOCK(&load_rpzs->search_lock);
+
+ /*
+ * Copy the other policy zones to the new summary databases
+ * unless there is only one policy zone.
+ */
+ if (rpzs->p.num_zones > 1) {
+ /*
+ * Copy to the radix tree.
+ */
+ load_pair.d = ~DNS_RPZ_ZBIT(rpz_num);
+ load_pair.ns = load_pair.d;
+ for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) {
+ new_pair.d = cnode->pair.d & load_pair.d;
+ new_pair.ns = cnode->pair.ns & load_pair.ns;
+ if (new_pair.d != 0 || new_pair.ns != 0) {
+ result = search(load_rpzs,
+ &cnode->ip, cnode->prefix,
+ &new_pair, ISC_TRUE,
+ &found);
+ if (result == ISC_R_NOMEMORY) {
+ load_unlock(rpzs, load_rpzsp);
+ return (result);
+ }
+ INSIST(result == ISC_R_SUCCESS);
+ }
+ /*
+ * Do down and to the left as far as possible.
+ */
+ next_cnode = cnode->child[0];
+ if (next_cnode != NULL)
+ continue;
+ /*
+ * Go up until we find a branch to the right where
+ * we previously took the branck to the left.
+ */
+ for (;;) {
+ parent_cnode = cnode->parent;
+ if (parent_cnode == NULL)
+ break;
+ if (parent_cnode->child[0] == cnode) {
+ next_cnode = parent_cnode->child[1];
+ if (next_cnode != NULL)
+ break;
+ }
+ cnode = parent_cnode;
+ }
+ }
+
+ /*
+ * Copy to the summary rbt.
+ */
+ dns_fixedname_init(&namef);
+ name = dns_fixedname_name(&namef);
+ dns_fixedname_init(&labelf);
+ label = dns_fixedname_name(&labelf);
+ dns_fixedname_init(&originf);
+ origin = dns_fixedname_name(&originf);
+ dns_rbtnodechain_init(&chain, NULL);
+ result = dns_rbtnodechain_first(&chain, rpzs->rbt, NULL, NULL);
+ while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
+ result = dns_rbtnodechain_current(&chain, label, origin,
+ &nmnode);
+ INSIST(result == ISC_R_SUCCESS);
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ new_data.pair.d = (nm_data->pair.d &
+ load_pair.d);
+ new_data.pair.ns = (nm_data->pair.ns &
+ load_pair.ns);
+ new_data.wild.d = (nm_data->wild.d &
+ load_pair.d);
+ new_data.wild.ns = (nm_data->wild.ns &
+ load_pair.ns);
+ if (new_data.pair.d != 0 ||
+ new_data.pair.ns != 0 ||
+ new_data.wild.d != 0 ||
+ new_data.wild.ns != 0) {
+ result = dns_name_concatenate(label,
+ origin, name, NULL);
+ INSIST(result == ISC_R_SUCCESS);
+ result = add_nm(load_rpzs, name,
+ &new_data);
+ if (result != ISC_R_SUCCESS) {
+ load_unlock(rpzs, load_rpzsp);
+ return (result);
+ }
+ }
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ }
+ if (result != ISC_R_NOMORE && result != ISC_R_NOTFOUND) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dns_rpz_ready(): unexpected %s",
+ isc_result_totext(result));
+ load_unlock(rpzs, load_rpzsp);
+ return (result);
+ }
+ }
+
+ fix_triggers(rpzs, &old_totals);
+ fix_triggers(load_rpzs, &new_totals);
+
+ dns_name_format(&load_rpzs->zones[rpz_num]->origin,
+ namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+ "reloading policy zone '%s' changed from"
+ " %d to %d qname, %d to %d nsdname,"
+ " %d to %d IP, %d to %d NSIP entries",
+ namebuf,
+ old_totals.qname, new_totals.qname,
+ old_totals.nsdname, new_totals.nsdname,
+ old_totals.ipv4 + old_totals.ipv6,
+ new_totals.ipv4 + new_totals.ipv6,
+ old_totals.nsipv4 + old_totals.nsipv6,
+ new_totals.nsipv4 + new_totals.nsipv6);
+
+ /*
+ * Exchange the summary databases.
+ */
+ LOCK(&rpzs->search_lock);
+
+ found = rpzs->cidr;
+ rpzs->cidr = load_rpzs->cidr;
+ load_rpzs->cidr = found;
+
+ rbt = rpzs->rbt;
+ rpzs->rbt = load_rpzs->rbt;
+ load_rpzs->rbt = rbt;
+
+ UNLOCK(&rpzs->search_lock);
+ load_unlock(rpzs, load_rpzsp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Add an IP address to the radix tree or a name to the summary database.
+ */
+isc_result_t
+dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_name_t *src_name) {
+ dns_rpz_zone_t *rpz;
+ dns_rpz_type_t rpz_type;
+ isc_result_t result;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ rpz_type = type_from_name(rpz, src_name);
+
+ LOCK(&rpzs->maint_lock);
+ LOCK(&rpzs->search_lock);
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_NSDNAME:
+ result = add_name(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_IP:
+ case DNS_RPZ_TYPE_NSIP:
+ result = add_cidr(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_BAD:
+ break;
+ }
+
+ UNLOCK(&rpzs->search_lock);
+ UNLOCK(&rpzs->maint_lock);
+ return (result);
+}
+
+/*
+ * Remove an IP address from the radix tree.
+ */
+static void
+del_cidr(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ isc_result_t result;
+ dns_rpz_cidr_key_t tgt_ip;
+ dns_rpz_prefix_t tgt_prefix;
+ dns_rpz_pair_zbits_t pair;
+ dns_rpz_cidr_node_t *tgt, *parent, *child;
+
+ /*
+ * Do not worry about invalid rpz IP address names. If we
+ * are here, then something relevant was added and so was
+ * valid. Invalid names here are usually internal RBTDB nodes.
+ */
+ result = name2ipkey(DNS_RPZ_DEBUG_QUIET, rpzs, rpz_num, rpz_type,
+ src_name, &tgt_ip, &tgt_prefix, &pair);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_FALSE, &tgt);
+ if (result != ISC_R_SUCCESS) {
+ INSIST(result == ISC_R_NOTFOUND ||
+ result == DNS_R_PARTIALMATCH);
+ /*
+ * Do not worry about missing summary RBT nodes that probably
+ * correspond to RBTDB nodes that were implicit RBT nodes
+ * that were later added for (often empty) wildcards
+ * and then to the RBTDB deferred cleanup list.
+ */
+ return;
}
+ /*
+ * Mark the node and its parents to reflect the deleted IP address.
+ * Do not count bits that are already clear for internal RBTDB nodes.
+ */
+ pair.d &= tgt->pair.d;
+ pair.ns &= tgt->pair.ns;
+ tgt->pair.d &= ~pair.d;
+ tgt->pair.ns &= ~pair.ns;
+ set_sum_pair(tgt);
+
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE);
+
/*
* We might need to delete 2 nodes.
*/
*/
if ((child = tgt->child[0]) != NULL) {
if (tgt->child[1] != NULL)
- return;
+ break;
} else {
child = tgt->child[1];
}
- if ((tgt->flags & (DNS_RPZ_CIDR_FG_IP_DATA |
- DNS_RPZ_CIDR_FG_NSIP_DATA)) != 0)
- return;
+ if (tgt->pair.d + tgt->pair.ns != 0)
+ break;
/*
* Replace the pointer to this node in the parent with
*/
parent = tgt->parent;
if (parent == NULL) {
- cidr->root = child;
+ rpzs->cidr = child;
} else {
parent->child[parent->child[1] == tgt] = child;
}
*/
if (child != NULL)
child->parent = parent;
- isc_mem_put(cidr->mctx, tgt, sizeof(*tgt));
+ isc_mem_put(rpzs->mctx, tgt, sizeof(*tgt));
tgt = parent;
} while (tgt != NULL);
}
+static void
+del_name(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_rpz_type_t rpz_type, dns_name_t *src_name)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t trig_namef;
+ dns_name_t *trig_name;
+ dns_rbtnode_t *nmnode;
+ dns_rpz_nm_data_t *nm_data, del_data;
+ isc_result_t result;
+
+ dns_fixedname_init(&trig_namef);
+ trig_name = dns_fixedname_name(&trig_namef);
+ name2data(rpzs, rpz_num, rpz_type, src_name, trig_name, &del_data);
+
+ /*
+ * No need for a summary database of names with only 1 policy zone.
+ */
+ if (rpzs->p.num_zones <= 1) {
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_FALSE);
+ return;
+ }
+
+ nmnode = NULL;
+ result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL, 0,
+ NULL, NULL);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Do not worry about missing summary RBT nodes that probably
+ * correspond to RBTDB nodes that were implicit RBT nodes
+ * that were later added for (often empty) wildcards
+ * and then to the RBTDB deferred cleanup list.
+ */
+ if (result == ISC_R_NOTFOUND)
+ return;
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz del_name(%s) node search failed: %s",
+ namebuf, isc_result_totext(result));
+ return;
+ }
+
+ nm_data = nmnode->data;
+ INSIST(nm_data != NULL);
+
+ /*
+ * Do not count bits that next existed for RBT nodes that would we
+ * would not have found in a summary for a single RBTDB tree.
+ */
+ del_data.pair.d &= nm_data->pair.d;
+ del_data.pair.ns &= nm_data->pair.ns;
+ del_data.wild.d &= nm_data->wild.d;
+ del_data.wild.ns &= nm_data->wild.ns;
+
+ nm_data->pair.d &= ~del_data.pair.d;
+ nm_data->pair.ns &= ~del_data.pair.ns;
+ nm_data->wild.d &= ~del_data.wild.d;
+ nm_data->wild.ns &= ~del_data.wild.ns;
+
+ if (nm_data->pair.d == 0 && nm_data->pair.ns == 0 &&
+ nm_data->wild.d == 0 && nm_data->wild.ns == 0) {
+ result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(src_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz del_name(%s) node delete failed: %s",
+ namebuf, isc_result_totext(result));
+ }
+ }
+
+ adj_trigger_cnt(rpzs, rpz_num, rpz_type, NULL, 0, ISC_FALSE);
+}
+
/*
- * Caller must hold tree lock.
- * Return ISC_R_NOTFOUND
- * or ISC_R_SUCCESS and the found entry's canonical and search names
- * and its prefix length
+ * Remove an IP address from the radix tree or a name from the summary database.
*/
-isc_result_t
-dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
- dns_rpz_type_t type, dns_name_t *canon_name,
- dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix)
+void
+dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num,
+ dns_name_t *src_name) {
+ dns_rpz_zone_t *rpz;
+ dns_rpz_type_t rpz_type;
+
+ REQUIRE(rpzs != NULL && rpz_num < rpzs->p.num_zones);
+ rpz = rpzs->zones[rpz_num];
+ REQUIRE(rpz != NULL);
+
+ rpz_type = type_from_name(rpz, src_name);
+
+ LOCK(&rpzs->maint_lock);
+ LOCK(&rpzs->search_lock);
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_NSDNAME:
+ del_name(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_IP:
+ case DNS_RPZ_TYPE_NSIP:
+ del_cidr(rpzs, rpz_num, rpz_type, src_name);
+ break;
+ case DNS_RPZ_TYPE_BAD:
+ break;
+ }
+
+ UNLOCK(&rpzs->search_lock);
+ UNLOCK(&rpzs->maint_lock);
+}
+
+/*
+ * Search the summary radix tree to get a relative owner name in a
+ * policy zone relevant to a triggering IP address.
+ * rpz_type and zbits limit the search for IP address netaddr
+ * return the policy zone's number or DNS_RPZ_INVALID_NUM
+ * ip_name is the relative owner name found and
+ * *prefixp is its prefix length.
+ */
+dns_rpz_num_t
+dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, const isc_netaddr_t *netaddr,
+ dns_name_t *ip_name, dns_rpz_prefix_t *prefixp)
{
dns_rpz_cidr_key_t tgt_ip;
- isc_result_t result;
+ dns_rpz_pair_zbits_t pair;
dns_rpz_cidr_node_t *found;
+ isc_result_t result;
+ dns_rpz_num_t rpz_num;
int i;
/*
tgt_ip.w[1] = 0;
tgt_ip.w[2] = ADDR_V4MAPPED;
tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr);
+ if (rpz_type == DNS_RPZ_TYPE_IP)
+ zbits &= rpzs->have.ipv4;
+ else
+ zbits &= rpzs->have.nsipv4;
} else if (netaddr->family == AF_INET6) {
dns_rpz_cidr_key_t src_ip6;
/*
* Given the int aligned struct in_addr member of netaddr->type
* one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *,
- * but there are objections.
+ * but some people object.
*/
memcpy(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w));
for (i = 0; i < 4; i++) {
tgt_ip.w[i] = ntohl(src_ip6.w[i]);
}
+ if (rpz_type == DNS_RPZ_TYPE_IP)
+ zbits &= rpzs->have.ipv6;
+ else
+ zbits &= rpzs->have.nsipv6;
} else {
- return (ISC_R_NOTFOUND);
+ return (DNS_RPZ_INVALID_NUM);
}
- result = search(cidr, &tgt_ip, 128, type, ISC_FALSE, &found);
- if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH)
- return (result);
+ if (zbits == 0)
+ return (DNS_RPZ_INVALID_NUM);
+ make_pair(&pair, zbits, rpz_type);
+
+ LOCK(&rpzs->search_lock);
+ result = search(rpzs, &tgt_ip, 128, &pair, ISC_FALSE, &found);
+ if (result == ISC_R_NOTFOUND) {
+ /*
+ * There are no eligible zones for this IP address.
+ */
+ UNLOCK(&rpzs->search_lock);
+ return (DNS_RPZ_INVALID_NUM);
+ }
+
+ /*
+ * Construct the trigger name for the longest matching trigger
+ * in the first eligible zone with a match.
+ */
+ *prefixp = found->prefix;
+ if (rpz_type == DNS_RPZ_TYPE_IP) {
+ INSIST((found->pair.d & pair.d) != 0);
+ rpz_num = zbit_to_num(found->pair.d & pair.d);
+ } else {
+ INSIST((found->pair.ns & pair.ns) != 0);
+ rpz_num = zbit_to_num(found->pair.ns & pair.ns);
+ }
+ result = ip2name(&found->ip, found->prefix, dns_rootname, ip_name);
+ UNLOCK(&rpzs->search_lock);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "rpz ip2name() failed: %s",
+ isc_result_totext(result));
+ return (DNS_RPZ_INVALID_NUM);
+ }
+ return (rpz_num);
+}
+
+/*
+ * Search the summary radix tree for policy zones with triggers matching
+ * a name.
+ */
+dns_rpz_zbits_t
+dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_name_t *trig_name)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_rbtnode_t *nmnode;
+ const dns_rpz_nm_data_t *nm_data;
+ dns_rpz_zbits_t found_zbits;
+ isc_result_t result;
+
+ if (zbits == 0)
+ return (0);
+
+ found_zbits = 0;
+
+ LOCK(&rpzs->search_lock);
+
+ nmnode = NULL;
+ result = dns_rbt_findnode(rpzs->rbt, trig_name, NULL, &nmnode, NULL,
+ DNS_RBTFIND_EMPTYDATA, NULL, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ found_zbits = nm_data->pair.d;
+ else
+ found_zbits = nm_data->pair.ns;
+ }
+ nmnode = nmnode->parent;
+ /* fall thru */
+ case DNS_R_PARTIALMATCH:
+ while (nmnode != NULL) {
+ nm_data = nmnode->data;
+ if (nm_data != NULL) {
+ if (rpz_type == DNS_RPZ_TYPE_QNAME)
+ found_zbits |= nm_data->wild.d;
+ else
+ found_zbits |= nm_data->wild.ns;
+ }
+ nmnode = nmnode->parent;
+ }
+ break;
+
+ case ISC_R_NOTFOUND:
+ break;
+
+ default:
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ */
+ dns_name_format(trig_name, namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dns_rpz_find_name(%s) failed: %s",
+ namebuf, isc_result_totext(result));
+ break;
+ }
- *prefix = found->bits;
- return (ip2name(cidr, &found->ip, found->bits, type,
- canon_name, search_name));
+ UNLOCK(&rpzs->search_lock);
+ return (zbits & found_zbits);
}
/*
isc_result_t result;
result = dns_rdataset_first(rdataset);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ INSIST(result == ISC_R_SUCCESS);
dns_rdataset_current(rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &cname, NULL);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ INSIST(result == ISC_R_SUCCESS);
dns_rdata_reset(&rdata);
/*
}
/*
- * CNAME PASSTHRU.origin means "do not rewrite.
+ * CNAME PASSTHRU. means "do not rewrite.
*/
if (dns_name_equal(&cname.cname, &rpz->passthru))
return (DNS_RPZ_POLICY_PASSTHRU);
NULL, /* resigned */
NULL, /* isdnssec */
NULL, /* getrrsetstats */
- NULL, /* rpz_enabled */
- NULL, /* rpz_findips */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
findnodeext,
findext,
NULL, /* setcachestats */
NULL, /* resigned */
NULL, /* isdnssec */
NULL, /* getrrsetstats */
- NULL, /* rpz_enabled */
- NULL, /* rpz_findips */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
findnodeext,
findext,
NULL, /* setcachestats */
view->v4_aaaa = dns_aaaa_ok;
view->v6_aaaa = dns_aaaa_ok;
view->aaaa_acl = NULL;
- ISC_LIST_INIT(view->rpz_zones);
- view->rpz_recursive_only = ISC_TRUE;
- view->rpz_break_dnssec = ISC_FALSE;
+ view->rpzs = NULL;
dns_fixedname_init(&view->dlv_fixed);
view->managed_keys = NULL;
view->redirect = NULL;
dns_acache_putdb(view->acache, view->cachedb);
dns_acache_detach(&view->acache);
}
- dns_rpz_view_destroy(view);
dns_rrl_view_destroy(view);
-
+ if (view->rpzs != NULL)
+ dns_rpz_detach_rpzs(&view->rpzs);
for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
dlzdb != NULL;
dlzdb = ISC_LIST_HEAD(view->dlz_searched)) {
}
#else
INSIST(view->acache == NULL);
- INSIST(ISC_LIST_EMPTY(view->rpz_zones));
+ INSIST(view->rpzs == NULL);
INSIST(ISC_LIST_EMPTY(view->dlz_searched));
INSIST(ISC_LIST_EMPTY(view->dlz_unsearched));
INSIST(view->rrl == NULL);
dns_db_overmem
dns_db_printnode
dns_db_register
-dns_db_rpz_enabled
-dns_db_rpz_findips
+dns_db_rpz_attach
+dns_db_rpz_ready
dns_db_setcachestats
dns_db_serialize
dns_db_subtractrdataset
dns_result_torcode
dns_result_totext
dns_rootns_create
+dns_rpz_add
+dns_rpz_attach_rpzs
+dns_rpz_beginload
dns_rpz_cidr_addip
-dns_rpz_cidr_deleteip
dns_rpz_cidr_find
-dns_rpz_cidr_free
dns_rpz_decode_cname
-dns_rpz_enabled
-dns_rpz_needed
-dns_rpz_new_cidr
+dns_rpz_delete
+dns_rpz_delete_node
+dns_rpz_detach_rpzs
+dns_rpz_find_ip
+dns_rpz_find_name
+dns_rpz_new_zones
dns_rpz_policy2str
-dns_rpz_set_need
+dns_rpz_ready
dns_rpz_str2policy
dns_rpz_type2str
-dns_rpz_view_destroy
dns_rriterator_current
dns_rriterator_destroy
dns_rriterator_first
dns_zone_forcereload
dns_zone_forwardupdate
dns_zone_fulldumptostream
+dns_zone_get_rpz_num
dns_zone_getadded
dns_zone_getchecknames
dns_zone_getclass
dns_zone_getraw
dns_zone_getqueryaoncl
dns_zone_getrequeststats
+dns_zone_getrpz_num
dns_zone_getserial
dns_zone_getserial2
dns_zone_getserialupdatemethod
dns_zone_refresh
dns_zone_rekey
dns_zone_replacedb
+dns_zone_rpz_attach
+dns_zone_rpz_enable
dns_zone_setacache
dns_zone_setadded
dns_zone_setalsonotify
isc_boolean_t added;
/*%
- * whether a rpz radix was needed when last loaded
+ * response policy data to be relayed to the database
*/
- isc_boolean_t rpz_zone;
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_num_t rpz_num;
/*%
* Serial number update method.
zone->nodes = 100;
zone->privatetype = (dns_rdatatype_t)0xffffU;
zone->added = ISC_FALSE;
- zone->rpz_zone = ISC_FALSE;
+ zone->rpzs = NULL;
+ zone->rpz_num = DNS_RPZ_INVALID_NUM;
ISC_LIST_INIT(zone->forwards);
zone->raw = NULL;
zone->secure = NULL;
zone_detachdb(zone);
if (zone->acache != NULL)
dns_acache_detach(&zone->acache);
+#ifdef BIND9
+ if (zone->rpzs != NULL) {
+ REQUIRE(zone->rpz_num < zone->rpzs->p.num_zones);
+ dns_rpz_detach_rpzs(&zone->rpzs);
+ zone->rpz_num = DNS_RPZ_INVALID_NUM;
+ }
+#endif
zone_freedbargs(zone);
RUNTIME_CHECK(dns_zone_setmasterswithkeys(zone, NULL, NULL, 0)
== ISC_R_SUCCESS);
}
+/*
+ * Set the response policy index and information for a zone.
+ */
+isc_result_t
+dns_zone_rpz_enable(dns_zone_t *zone, dns_rpz_zones_t *rpzs,
+ dns_rpz_num_t rpz_num)
+{
+ /*
+ * Only RBTDB zones can be used for response policy zones,
+ * because only they have the code to load the create the summary data.
+ * Only zones that are loaded instead of mmap()ed create the
+ * summary data and so can be policy zones.
+ */
+ if (strcmp(zone->db_argv[0], "rbt") != 0 &&
+ strcmp(zone->db_argv[0], "rbt64") != 0)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ /*
+ * This must happen only once or be redundant.
+ */
+ LOCK_ZONE(zone);
+ if (zone->rpzs != NULL) {
+ REQUIRE(zone->rpzs == rpzs && zone->rpz_num == rpz_num);
+ } else {
+ REQUIRE(zone->rpz_num == DNS_RPZ_INVALID_NUM);
+ dns_rpz_attach_rpzs(rpzs, &zone->rpzs);
+ zone->rpz_num = rpz_num;
+ }
+ rpzs->defined |= DNS_RPZ_ZBIT(rpz_num);
+ UNLOCK_ZONE(zone);
+
+ return (ISC_R_SUCCESS);
+}
+
+dns_rpz_num_t
+dns_zone_get_rpz_num(dns_zone_t *zone) {
+ return (zone->rpz_num);
+}
+
static isc_result_t
zone_load(dns_zone_t *zone, unsigned int flags) {
isc_result_t result;
* "rndc reconfig", we are done.
*/
if (!isc_time_isepoch(&zone->loadtime) &&
- (flags & DNS_ZONELOADFLAG_NOSTAT) != 0 &&
- zone->rpz_zone == dns_rpz_needed()) {
+ (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) {
result = ISC_R_SUCCESS;
goto cleanup;
}
if (result == ISC_R_SUCCESS) {
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) &&
- isc_time_compare(&filetime, &zone->loadtime) <= 0 &&
- zone->rpz_zone == dns_rpz_needed()) {
+ isc_time_compare(&filetime, &zone->loadtime) <= 0) {
dns_zone_log(zone, ISC_LOG_DEBUG(1),
"skipping load: master file "
"older than last load");
goto cleanup;
}
loadtime = filetime;
- zone->rpz_zone = dns_rpz_needed();
}
}
isc_result_t tresult;
unsigned int options;
- options = get_master_options(zone);
+#ifdef BIND9
+ if (zone->rpz_num != DNS_RPZ_INVALID_NUM) {
+ REQUIRE(zone->rpzs != NULL);
+ dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num);
+ }
+#endif
+ options = get_master_options(zone);
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MANYERRORS))
options |= DNS_MASTER_MANYERRORS;
if (result != ISC_R_SUCCESS)
goto cleanup;
} else {
+#ifdef BIND9
+ result = dns_db_rpz_ready(db);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
zone_attachdb(zone, db);
ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
DNS_ZONE_SETFLAG(zone,
REQUIRE(DNS_ZONE_VALID(zone));
REQUIRE(LOCKED_ZONE(zone));
+#ifdef BIND9
+ result = dns_db_rpz_ready(db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+#endif
+
result = zone_get_from_db(zone, db, &nscount, &soacount,
NULL, NULL, NULL, NULL, NULL, NULL);
if (result == ISC_R_SUCCESS) {
* response-policy {
* zone <string> [ policy (given|disabled|passthru|
* nxdomain|nodata|cname <domain> ) ]
- * [ recursive-only yes|no ]
- * [ max-policy-ttl number ] ;
- * } [ recursive-only yes|no ] [ break-dnssec yes|no ]
- * [ max-policy-ttl number ] ;
+ * [ recursive-only yes|no ] [ max-policy-ttl number ] ;
+ * } [ recursive-only yes|no ] [ max-policy-ttl number ] ;
+ * [ break-dnssec yes|no ] [ min-ns-dots number ]
+ * [ qname-wait-recurse yes|no ]
*/
static void
{ "recursive-only", &cfg_type_boolean, 0 },
{ "break-dnssec", &cfg_type_boolean, 0 },
{ "max-policy-ttl", &cfg_type_uint32, 0 },
+ { "min-ns-dots", &cfg_type_uint32, 0 },
+ { "qname-wait-recurse", &cfg_type_boolean, 0 },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_rpz = {