+3620. [func] Added "rpz-client-ip" policy triggers, enabling
+ RPZ responses to be configured on the basis of
+ the client IP address; this can be used, for
+ example, to blacklist misbehaving recursive
+ or stub resolvers. [RT #33605]
+
+3619. [bug] Fixed a bug in RPZ with "recursive-only no;"
+ [RT #33776]
+
3618. [func] "rndc reload" now checks modification times of
include files as well as master files to determine
whether to skip reloading a zone. [RT #33936]
{
char qnamebuf[DNS_NAME_FORMATSIZE];
char p_namebuf[DNS_NAME_FORMATSIZE];
+ const char *failed;
if (!isc_log_wouldlog(ns_g_lctx, level))
return;
/*
- * bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed" for problems.
*/
+ if (level <= DNS_RPZ_DEBUG_LEVEL1)
+ failed = "failed: ";
+ else
+ 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",
+ "rpz %s rewrite %s via %s%s%s%s",
dns_rpz_type2str(rpz_type),
- qnamebuf, p_namebuf, str, isc_result_totext(result));
+ qnamebuf, p_namebuf,
+ str, failed, isc_result_totext(result));
}
/*
return (ISC_R_SUCCESS);
}
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
- "query_getzonedb() ", result);
+ " query_getzonedb()", result);
return (result);
}
rpzs = client->view->rpzs;
switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits = rpzs->have.client_ip;
+ break;
case DNS_RPZ_TYPE_QNAME:
zbits = rpzs->have.qname;
break;
result = st->r.r_result;
if (result == DNS_R_DELEGATION) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
- rpz_type, "rpz_rrset_find(1) ", result);
+ rpz_type, " rpz_rrset_find(1)", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
result = DNS_R_SERVFAIL;
}
&version, &is_zone);
if (result != ISC_R_SUCCESS) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
- rpz_type, "rpz_rrset_find(2) ", result);
+ rpz_type, " rpz_rrset_find(2)", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
if (zone != NULL)
dns_zone_detach(&zone);
* from being too long.
*/
switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ suffix = &rpz->client_ip;
+ break;
case DNS_RPZ_TYPE_QNAME:
suffix = &rpz->origin;
break;
*/
if (labels-first < 2) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, suffix,
- rpz_type, "concatentate() ", result);
+ rpz_type, " concatentate()", result);
return (ISC_R_FAILURE);
}
/*
*/
if (first == 0) {
rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, suffix,
- rpz_type, "concatentate() ", result);
+ rpz_type, " concatentate()", result);
}
++first;
}
/*
* 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 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.
&rdsiter);
if (result != ISC_R_SUCCESS) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name,
- rpz_type, "allrdatasets() ", result);
+ rpz_type, " allrdatasets()", result);
return (DNS_R_SERVFAIL);
}
for (result = dns_rdatasetiter_first(rdsiter);
if (result != ISC_R_NOMORE) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
p_name, rpz_type,
- "rdatasetiter ", result);
+ " rdatasetiter", result);
return (DNS_R_SERVFAIL);
}
/*
*
* With more than one applicable policy, prefer
* the earliest configured policy,
- * QNAME over IP over NSDNAME over NSIP,
+ * client-IP over 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.
break;
/*
- * Stop checking after saving an enabled hit.
+ * Stop checking after saving an enabled hit in this
+ * policy zone. The radix tree in the policy zone
+ * ensures that we found the longest match.
*/
if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
rpz_save_p(st, rpz, rpz_type,
case DNS_R_CNAME:
case DNS_R_DNAME:
rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type,
- "NS address rewrite rrset ", result);
+ " NS address rewrite rrset", result);
return (ISC_R_SUCCESS);
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 ",
+ rpz_type, " NS address rewrite rrset",
result);
}
return (DNS_R_SERVFAIL);
/*
* With more than one applicable policy, prefer
* the earliest configured policy,
- * QNAME over IP over NSDNAME over NSIP,
+ * client-IP over QNAME over IP over NSDNAME over NSIP,
* and the smallest name.
* We known st->m.rpz->num >= rpz->num and either
* st->m.rpz->num > rpz->num or st->m.type >= rpz_type
break;
case DNS_R_DELEGATION:
case ISC_R_NOTFOUND:
- qresult_type = 2;
+ /*
+ * If recursion is on, do only tentative rewriting.
+ * If recursion is off, this the normal and only time we
+ * can rewrite.
+ */
+ if (RECURSIONOK(client))
+ qresult_type = 2;
+ else
+ qresult_type = 1;
break;
case ISC_R_FAILURE:
case ISC_R_TIMEDOUT:
case DNS_R_BROKENCHAIN:
rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
DNS_RPZ_TYPE_QNAME,
- "stop on qresult in rpz_rewrite() ", qresult);
+ " stop on qresult in rpz_rewrite()", qresult);
return (ISC_R_SUCCESS);
default:
rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
DNS_RPZ_TYPE_QNAME,
- "stop on unrecognized qresult in rpz_rewrite() ",
+ " stop on unrecognized qresult in rpz_rewrite()",
qresult);
return (ISC_R_SUCCESS);
}
rdataset = NULL;
- if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
- /*
- * 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.
- */
+
+ if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) !=
+ (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) {
+ isc_netaddr_t netaddr;
+ dns_rpz_zbits_t allowed;
+
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.
+ * Get bits for the policy zones that do not need
+ * to wait for the results of recursion.
*/
- zbits = client->view->rpzs->have.qname_skip_recurse;
- if (zbits == 0)
+ allowed = client->view->rpzs->have.qname_skip_recurse;
+ if (allowed == 0)
return (ISC_R_SUCCESS);
} else {
- zbits = DNS_RPZ_ALL_ZBITS;
+ allowed = 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.
+ * Check once for triggers for the client IP address.
+ */
+ if ((st->state & DNS_RPZ_DONE_CLIENT_IP) == 0) {
+ zbits = rpz_get_zbits(client, dns_rdatatype_none,
+ DNS_RPZ_TYPE_CLIENT_IP);
+ zbits &= allowed;
+ if (zbits != 0) {
+ isc_netaddr_fromsockaddr(&netaddr,
+ &client->peeraddr);
+ result = rpz_rewrite_ip(client, &netaddr, qtype,
+ DNS_RPZ_TYPE_CLIENT_IP,
+ zbits, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+
+ /*
+ * 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
+ */
+ if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+ result = rpz_rewrite_name(client, client->query.qname,
+ qtype, DNS_RPZ_TYPE_QNAME,
+ allowed, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Check IPv4 addresses in A RRs next.
+ * Reset to the start of the NS names.
+ */
+ st->r.label = dns_name_countlabels(client->query.qname);
+ st->state &= ~(DNS_RPZ_DONE_QNAME_IP |
+ DNS_RPZ_DONE_IPv4);
+
+ }
+
+ /*
+ * Quit if this was an attempt to find a qname or
+ * client-IP trigger before recursion.
+ * We will be back if no pre-recursion triggers hit.
+ * For example, consider 2 policy zones, both with qname and
+ * IP address triggers. If the qname misses the 1st zone,
+ * then we cannot know whether a hit for the qname in the
+ * 2nd zone matters until after recursing to get the A RRs and
+ * testing them in the first zone.
+ * Do not bother saving the work from this attempt,
+ * because recusion is so slow.
*/
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.
+ * DNS_RPZ_DONE_QNAME but not DNS_RPZ_DONE_CLIENT_IP
+ * is reset at the end of dealing with each CNAME.
*/
- st->state &= ~(DNS_RPZ_DONE_QNAME_IP | DNS_RPZ_DONE_IPv4);
- st->state |= DNS_RPZ_DONE_QNAME;
+ st->state |= (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME);
}
/*
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,
+ client->query.qname, qtype,
+ DNS_RPZ_TYPE_IP,
&rdataset, resuming);
if (result != ISC_R_SUCCESS)
goto cleanup;
case ISC_R_FAILURE:
rpz_rewrite_ns_skip(client, nsname, result,
DNS_RPZ_DEBUG_LEVEL3,
- "NS db_find() ");
+ " NS db_find()");
continue;
default:
rpz_rewrite_ns_skip(client, nsname, result,
DNS_RPZ_INFO_LEVEL,
- "unrecognized NS db_find() ");
+ " unrecognized NS db_find()");
continue;
}
}
if (result != ISC_R_SUCCESS) {
rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
nsname, DNS_RPZ_TYPE_NSIP,
- "rdata_tostruct() ", result);
+ " rdata_tostruct()", result);
st->m.policy = DNS_RPZ_POLICY_ERROR;
goto cleanup;
}
rpz_st->state |= DNS_RPZ_REWRITTEN;
if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU &&
+ (rpz_st->m.policy != DNS_RPZ_POLICY_TCP_ONLY ||
+ (client->attributes & NS_CLIENTATTR_TCP) == 0) &&
rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
- if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) {
- result = dns_name_copy(client->query.qname,
- fname, NULL);
- RUNTIME_CHECK(result == ISC_R_SUCCESS);
- }
+ /* We got a hit and are going to answer with our
+ * fiction. Ensure that we answer with the name
+ * we looked up even if we were stopped short
+ * in recursion or for a deferral.
+ */
+ rresult = dns_name_copy(client->query.qname,
+ fname, NULL);
+ RUNTIME_CHECK(rresult == ISC_R_SUCCESS);
rpz_clean(&zone, &db, &node, NULL);
if (rpz_st->m.rdataset != NULL) {
query_putrdataset(client, &rdataset);
rpz_st->m.zone = NULL;
switch (rpz_st->m.policy) {
+ case DNS_RPZ_POLICY_TCP_ONLY:
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ if (result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NCACHENXDOMAIN)
+ client->message->rcode =
+ dns_rcode_nxdomain;
+ else
+ result = ISC_R_SUCCESS;
+ rpz_log_rewrite(client, ISC_FALSE,
+ rpz_st->m.policy,
+ rpz_st->m.type, zone,
+ rpz_st->p_name);
+ goto cleanup;
+ case DNS_RPZ_POLICY_DROP:
+ result = ISC_R_SUCCESS;
+ QUERY_ERROR(DNS_R_DROP);
+ break;
case DNS_RPZ_POLICY_NXDOMAIN:
result = DNS_R_NXDOMAIN;
break;
result != DNS_R_CNAME) {
/*
* We will add all of the rdatasets of
- * the node by iterating, setting the
- * TTL then.
+ * the node by iterating later,
+ * and set the TTL then.
*/
if (dns_rdataset_isassociated(rdataset))
dns_rdataset_disassociate(rdataset);
return (result);
}
dns_name_init(&new->origin, NULL);
+ dns_name_init(&new->client_ip, NULL);
dns_name_init(&new->ip, NULL);
dns_name_init(&new->nsdname, NULL);
dns_name_init(&new->nsip, NULL);
dns_name_init(&new->passthru, NULL);
+ dns_name_init(&new->drop, NULL);
+ dns_name_init(&new->tcp_only, NULL);
dns_name_init(&new->cname, NULL);
new->num = view->rpzs->p.num_zones++;
view->rpzs->zones[new->num] = new;
if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin))
*old_rpz_okp = ISC_FALSE;
+ result = configure_rpz_name2(view, rpz_obj, &new->client_ip,
+ DNS_RPZ_CLIENT_IP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
result = configure_rpz_name2(view, rpz_obj, &new->ip,
DNS_RPZ_IP_ZONE, &new->origin);
if (result != ISC_R_SUCCESS)
return (result);
result = configure_rpz_name(view, rpz_obj, &new->passthru,
- DNS_RPZ_PASSTHRU_ZONE, "zone");
+ DNS_RPZ_PASSTHRU_NAME, "name");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->drop,
+ DNS_RPZ_DROP_NAME, "name");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->tcp_only,
+ DNS_RPZ_TCP_ONLY_NAME, "name");
if (result != ISC_R_SUCCESS)
return (result);
; performance test
tld5. NS ns.tld5.
ns.tld5. A 10.53.0.5
+
+; generate SERVFAIL
+servfail NS ns.tld2.
; Its contents are also changed with nsupdate
-$TTL 120
+$TTL 300
@ SOA blx. hostmaster.ns.blx. ( 1 3600 1200 604800 60 )
- NS ns
-ns A 10.53.0.3
+ NS ns.tld3.
-; Poke the radix tree a little.
-128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip CNAME .
-128.1111.2222.3333.4444.5555.6666.zz.rpz-ip CNAME .
-128.1111.2222.3333.4444.5555.zz.8888.rpz-ip CNAME .
-128.1111.2222.3333.4444.zz.8888.rpz-ip CNAME .
-128.zz.3333.4444.0.0.8888.rpz-ip CNAME .
-128.zz.3333.4444.0.7777.8888.rpz-ip CNAME .
-128.zz.3333.4444.0.8777.8888.rpz-ip CNAME .
-127.zz.3333.4444.0.8777.8888.rpz-ip CNAME .
-
-
-; regression testing for some old crashes
-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.
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
+ zone "bl-drop" policy drop;
+ zone "bl-tcp-only" policy tcp-only;
+ }
+ min-ns-dots 0
+ qname-wait-recurse yes
;
};
allow-update {any;};};
zone "bl-garden." {type master; file "bl-garden.db";
allow-update {any;};};
+zone "bl-drop." {type master; file "bl-drop.db";
+ allow-update {any;};};
+zone "bl-tcp-only." {type master; file "bl-tcp-only.db";
+ allow-update {any;};};
zone "crash1.tld2" {type master; file "crash1";};
zone "crash2.tld3." {type master; file "crash2";};
# run the performace test close to real life
--c named.conf -g
+-c named.conf -gd3
sh clean.sh
-# set up test policy zones. bl-2 is used to check competing zones.
-# bl-{given,disabled,passthru,no-data,nxdomain,cname,wildcard,garden}
-# are used to check policy overrides in named.conf.
-# NO-OP is an obsolete synonym for PASSHTRU
-for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden; do
+# set up test policy zones.
+# bl is the main test zone
+# bl-2 is used to check competing zones.
+# bl-{given,disabled,passthru,no-data,nxdomain,cname,wildcard,garden,
+# drop,tcp-only} are used to check policy overrides in named.conf.
+# NO-OP is an obsolete synonym for PASSHTRU
+for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden -drop -tcp-only; do
sed -e "/SOA/s/blx/bl$NM/g" ns3/base.db >ns3/bl$NM.db
done
signzone ns2 tld2s. base-tld2s.db tld2s.db
-# Performance checks.
+# Performance and a few other checks.
cat <<EOF >ns5/rpz-switch
response-policy {
zone "bl0"; zone "bl1"; zone "bl2"; zone "bl3"; zone "bl4";
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;
+ max-policy-ttl 90
+ break-dnssec yes
+ qname-wait-recurse no
+ ;
EOF
cat <<EOF >ns5/example.db
-\$TTL 120
+\$TTL 300
@ SOA . hostmaster.ns.example.tld5. ( 1 3600 1200 604800 60 )
NS ns
NS ns1
EOF
cat <<EOF >ns5/bl.db
-\$TTL 120
+\$TTL 300
@ SOA . hostmaster.ns.blperf. ( 1 3600 1200 604800 60 )
- NS ns
-ns A 10.53.0.5
+ NS ns.tld5.
-; used only in failure for "recursive-only no" in #8 test5
-a3-5.tld2 CNAME *.
+; for "qname-wait-recurse no" in #35 test1
+x.servfail A 35.35.35.35
+; for "recursive-only no" in #8 test5
+a3-5.tld2 CNAME .
; for "break-dnssec" in #9 & #10 test5
-a3-5.tld2s CNAME *.
+a3-5.tld2s CNAME .
; for "max-policy-ttl 90" in #17 test5
a3-17.tld2 500 A 17.17.17.17
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
+ # Do not build the full zones if we will not use them.
$PERL -e 'for ($val = 1; $val <= 65535; ++$val) {
printf("host-%05d\tA 192.168.%d.%d\n", $val, $val/256, $val%256);
}' >>ns5/example.db
; DO=1 without signatures, DO=0 with signatures are rewritten
; 26 - 27
update add a0-1.tld2s.bl. 300 CNAME .
+; 32
+update add a3-8.tld2.bl. 300 CNAME rpz-drop.
+; 33
+update add a3-9.tld2.bl. 300 CNAME rpz-tcp-only.
+; 34 qname-wait-recurse yes
+update add x.servfail.bl. 300 A 127.0.0.34
send
send
update add c2.crash2.tld3.bl-2 300 A 127.0.0.16
send
+
+; client-IP address trigger
+; 17
+update add 32.1.0.53.10.rpz-client-ip.bl 300 A 127.0.0.17
+send
; 4
update add a3-4.tld2.bl-disabled. 300 A 127.0.0.4
send
-; 5 - 8
+; 5 - 7
update add a3-5.tld2.bl-nodata. 300 A 127.0.0.5
-; 9 - 10
-update add a3-5.tld2s.bl-nodata. 300 A 127.0.0.9
send
; 11
update add a3-6.tld2.bl-nxdomain. 300 A 127.0.0.11
; 16
update add a3-16.tld2.bl. 300 A 127.0.0.16
send
+; 18
+update add a3-18.tld2.bl-drop. 300 A 127.0.0.18
+send
+; 19
+update add a3-19.tld2.bl-tcp-only. 300 A 127.0.0.19
+send
--- /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'
+
+server 10.53.0.3 5300
+
+; Poke the radix tree a little.
+update add 128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip.bl. 300 CNAME .
+update add 128.1111.2222.3333.4444.5555.6666.zz.rpz-ip.bl. 300 CNAME .
+update add 128.1111.2222.3333.4444.5555.zz.8888.rpz-ip.bl. 300 CNAME .
+update add 128.1111.2222.3333.4444.zz.8888.rpz-ip.bl. 300 CNAME .
+update add 128.zz.3333.4444.0.0.8888.rpz-ip.bl. 300 CNAME .
+update add 128.zz.3333.4444.0.7777.8888.rpz-ip.bl. 300 CNAME .
+update add 128.zz.3333.4444.0.8777.8888.rpz-ip.bl. 300 CNAME .
+update add 127.zz.3333.4444.0.8777.8888.rpz-ip.bl. 300 CNAME .
+;
+;
+; regression testing for some old crashes
+update add redirect.bl. 300 A 127.0.0.1
+update add *.redirect.bl. 300 A 127.0.0.1
+update add *.credirect.bl. 300 CNAME google.com.
+;
+send
ns=10.53.0
ns1=$ns.1 # root, defining the others
-ns2=$ns.2 # authoritative server whose answers are rewritten
-ns3=$ns.3 # resolver that does the rewriting
+ns2=$ns.2 # authoritative server whose records are rewritten
+ns3=$ns.3 # main rewriting resolver
ns4=$ns.4 # another authoritative server that is rewritten
-ns5=$ns.5 # resolver to check performance and give un-rewritten
- # responses for comparisons
+ns5=$ns.5 # another rewriting resolver
HAVE_CORE=
SAVE_RESULTS=
-NS3_STATS=47
+
USAGE="$0: [-x]"
while getopts "x" c; do
RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
digcmd () {
- 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 || \
- digcmd_args="+noauth $digcmd_args"
+ if test "$1" = TCP; then
+ shift
+ fi
+ # Default to +noauth and @$ns3
+ # Also default to -bX where X is the @value so that OS X will choose
+ # the right IP source address.
+ digcmd_args=`echo "+noadd +time=1 +tries=1 -p 5300 $*" | \
+ sed -e "/@/!s/.*/& @$ns3/" \
+ -e '/-b/!s/@\([^ ]*\)/@\1 -b\1/' \
+ -e '/+n?o?auth/!s/.*/+noauth &/'`
#echo I:dig $digcmd_args 1>&2
$DIG $digcmd_args
}
# (re)load the reponse policy zones with the rules in the file $TEST_FILE
load_db () {
if test -n "$TEST_FILE"; then
- $NSUPDATE -v $TEST_FILE || {
+ if $NSUPDATE -v $TEST_FILE; then :
+ $RNDCCMD $ns3 sync
+ else
echo "I:failed to update policy zone with $TEST_FILE"
+ $RNDCCMD $ns3 sync
exit 1
- }
+ fi
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"
+ HOST=$1
+ LABEL="$2"
+ NSDIR="$3"
+ EXPECTED="$4"
+ $RNDCCMD $HOST stats
+ NEW_CNT=0`sed -n -e 's/[ ]*\([0-9]*\).response policy.*/\1/p' \
+ $NSDIR/named.stats | tail -1`
+ eval "OLD_CNT=0\$${NSDIR}_CNT"
+ GOT=`expr $NEW_CNT - $OLD_CNT`
+ if test "$GOT" -ne "$EXPECTED"; then
+ setret "I:wrong $LABEL $NSDIR statistics of $GOT instead of $EXPECTED"
fi
+ eval "${NSDIR}_CNT=$NEW_CNT"
}
# $1=message $2=optional test file name
ckresult () {
#ckalive "$1" "I:server crashed by 'dig $1'" || return 1
if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then
+ NEED_TCP=`echo "$1" | sed -n -e 's/[Tt][Cc][Pp].*/TCP/p'`
+ RESULT_TCP=`sed -n -e 's/.*Truncated, retrying in TCP.*/TCP/p' $DIGNM`
+ if test "$NEED_TCP" != "$RESULT_TCP"; then
+ setret "I:'dig $1' wrong; no or unexpected truncation in $DIGNM"
+ return 1
+ fi
clean_result ${DIGNM}*
return 0
fi
clean_result ${DIGNM}*
}
-# check that a response is not rewritten
-# $1=target domain $2=optional query type
+# Check that a response is not rewritten
+# Use $ns1 instead of the authority for most test domains, $ns2 to prevent
+# spurious differences for `dig +norecurse`
+# $1=optional "TCP" remaining args for dig
nochange () {
make_dignm
digcmd $* >$DIGNM
- digcmd $* @$ns5 >${DIGNM}_OK
+ digcmd $* @$ns1 >${DIGNM}_OK
ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK
}
ckresult "$*" ${DIGNM}_OK
}
+# check dropped response
+DROPPED='^;; connection timed out; no servers could be reached'
+drop () {
+ make_dignm
+ digcmd $* >$DIGNM
+ if grep "$DROPPED" $DIGNM >/dev/null; then
+ clean_result ${DIGNM}*
+ return 0
+ fi
+ setret "I:'dig $1' wrong; response in $DIGNM"
+ return 1
+}
+
+
# make prototype files to check against rewritten results
digcmd nonexistent @$ns2 >proto.nxdomain
digcmd txt-only.tld2 @$ns2 >proto.nodata
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 +nodnssec # 31
+drop a3-8.tld2 # 32 drop
+nochange tcp a3-9.tld2 # 33 tcp-only
+here x.servfail <<'EOF' # 34 qname-wait-recurse yes
+ ;; status: SERVFAIL, x
+EOF
+addr 35.35.35.35 "x.servfail @$ns5" # 35 qname-wait-recurse no
end_group
+ckstats $ns3 test1 ns3 22
+ckstats $ns5 test1 ns5 1
start_group "IP rewrites" test2
nodata a3-1.tld2 # 1 NODATA
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
+addr 127.0.0.17 "a4-4.tld2 -b $ns1" # 17 client-IP address trigger
end_group
+ckstats $ns3 test2 ns3 10
# check that IP addresses for previous group were deleted from the radix tree
start_group "radix tree deletions"
nochange a4-1-aaaa.tld2 -tAAAA
nochange a5-1-2.tld2
end_group
+ckstats $ns3 'radix tree deletions' ns3 0
if ./rpz nsdname; then
# these tests assume "min-ns-dots 0"
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`
+ ckstats $ns3 test3 ns3 7
else
echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
fi
a3-1.tld2. x IN TXT "NSIP walled garden"
EOF
end_group
- NS3_STATS=`expr $NS3_STATS + 4`
+ ckstats $ns3 test4 ns3 4
else
echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
fi
nochange a3-2.tld2 # 2 bl-passthru
nochange a3-3.tld2 # 3 bl-no-op obsolete for passthru
nochange a3-4.tld2 # 4 bl-disabled
-nodata a3-5.tld2 # 5 bl-nodata
-nodata a3-5.tld2 +norecurse # 6 bl-nodata recursive-only no
-nodata a3-5.tld2 # 7 bl-nodata
-nodata a3-5.tld2 +norecurse @$ns5 # 8 bl-nodata recursive-only no
-nodata a3-5.tld2s @$ns5 # 9 bl-nodata
-nodata a3-5.tld2s +dnssec @$ns5 # 10 bl-nodata break-dnssec
+nodata a3-5.tld2 # 5 bl-nodata zone recursive-only no
+nodata a3-5.tld2 +norecurse # 6 bl-nodata zone recursive-only no
+nodata a3-5.tld2 # 7 bl-nodata not needed
+nxdomain a3-5.tld2 +norecurse @$ns5 # 8 bl-nodata global recursive-only no
+nxdomain a3-5.tld2s @$ns5 # 9 bl-nodata global break-dnssec
+nxdomain a3-5.tld2s +dnssec @$ns5 # 10 bl-nodata global break-dnssec
nxdomain a3-6.tld2 # 11 bl-nxdomain
here a3-7.tld2 -tany <<'EOF'
;; status: NOERROR, x
addr 12.12.12.12 a3-15.tld2 # 15 bl-garden via CNAME to a12.tld2
addr 127.0.0.16 a3-16.tld2 100 # 16 bl max-policy-ttl 100
addr 17.17.17.17 "a3-17.tld2 @$ns5" 90 # 17 ns5 bl max-policy-ttl 90
+drop a3-18.tld2 # 18 bl-drop
+nxdomain TCP a3-19.tld2 # 19 bl-tcp-only
end_group
+ckstats $ns3 test5 ns3 12
+ckstats $ns5 test5 ns5 4
+
# check that miscellaneous bugs are still absent
-start_group "crashes"
+start_group "crashes" test6
for Q in RRSIG SIG ANY 'ANY +dnssec'; do
nocrash a3-1.tld2 -t$Q
nocrash a3-2.tld2 -t$Q
# resolving foo.
# nxdomain 32.3.2.1.127.rpz-ip
end_group
+ckstats $ns3 bugs ns3 8
+
# superficial test for major performance bugs
$QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >/dev/null
comment "before real test $1"
PFILE="ns5/$2.perf"
+ $RNDCCMD $ns5 notrace
$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 \
}
# get qps with rpz
- perf 'with rpz' rpz 'NOERROR:2900 NXDOMAIN:100 '
+ perf 'with RPZ' rpz 'NOERROR:2900 NXDOMAIN:100 '
RPZ=`trim rpz`
# turn off rpz and measure qps again
- echo "# rpz off" >ns5/rpz-switch
+ echo "# RPZ off" >ns5/rpz-switch
RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
- perf 'without rpz' norpz 'NOERROR:3000 '
+ perf 'without RPZ' norpz 'NOERROR:3000 '
NORPZ=`trim norpz`
PERCENT=`expr \( "$RPZ" \* 100 + \( $NORPZ / 2 \) \) / $NORPZ`
- echo "I:$RPZ qps with rpz is $PERCENT% of $NORPZ qps without rpz"
+ 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% of $NORPZ qps without RPZ is too high"
fi
- ckstats $ns5 ns5 203
+ ckstats $ns5 performance ns5 200
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
<optional> min-table-size <replaceable>number</replaceable> ; </optional>
} ; </optional>
<optional> response-policy { <replaceable>zone_name</replaceable>
- <optional> policy given | disabled | passthru | nxdomain | nodata | cname <replaceable>domain</replaceable> </optional>
+ <optional> policy given | disabled | passthru | drop | 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> min-ns-dots <replaceable>number</replaceable> </optional>
</para>
<para>
- Four policy triggers are encoded in RPZ records, QNAME, IP, NSIP,
- and NSDNAME.
- QNAME RPZ records triggered by query names of requests and targets
- of CNAME records resolved to generate the response.
- The owner name of a QNAME RPZ record is the query name relativized
- to the RPZ.
- </para>
+ Five policy triggers can be encoded in RPZ records.
+ <variablelist>
+ <varlistentry>
+ <term><command>RPZ-CLIENT-IP</command></term>
+ <listitem>
+ <para>
+ IP records are triggered by the IP address of the
+ DNS client.
+ Client IP address triggers are encoded in records that have
+ owner names that are subdomains of
+ <command>rpz-client-ip</command> relativized to the
+ policy zone origin name
+ and encode an address or address block.
+ IPv4 addresses are represented as
+ <userinput>prefixlength.B4.B3.B2.B1.rpz-ip</userinput>.
+ The IPv4 prefix length must be between 1 and 32.
+ All four bytes, B4, B3, B2, and B1, must be present.
+ B4 is the decimal value of the least significant byte of the
+ IPv4 address as in IN-ADDR.ARPA.
+ </para>
- <para>
- The second kind of RPZ trigger is an IP address in an A and AAAA
- record in the ANSWER section of a response.
- IP address triggers are encoded in records that have owner names
- that are subdomains of <userinput>rpz-ip</userinput> relativized
- to the RPZ origin name and encode an IP address or address block.
- IPv4 trigger addresses are represented as
- <userinput>prefixlength.B4.B3.B2.B1.rpz-ip</userinput>.
- The prefix length must be between 1 and 32.
- All four bytes, B4, B3, B2, and B1, must be present.
- B4 is the decimal value of the least significant byte of the
- IPv4 address as in IN-ADDR.ARPA.
- IPv6 addresses are encoded in a format similar to the standard
- IPv6 text representation,
- <userinput>prefixlength.W8.W7.W6.W5.W4.W3.W2.W1.rpz-ip</userinput>.
- Each of W8,...,W1 is a one to four digit hexadecimal number
- representing 16 bits of the IPv6 address as in the standard text
- representation of IPv6 addresses, but reversed as in IN-ADDR.ARPA.
- All 8 words must be present except when consecutive
- zero words are replaced with <userinput>.zz.</userinput>
- analogous to double colons (::) in standard IPv6 text encodings.
- The prefix length must be between 1 and 128.
- </para>
+ <para>
+ IPv6 addresses are encoded in a format similar
+ to the standard IPv6 text representation,
+ <userinput>prefixlength.W8.W7.W6.W5.W4.W3.W2.W1.rpz-ip</userinput>.
+ Each of W8,...,W1 is a one to four digit hexadecimal number
+ representing 16 bits of the IPv6 address as in the standard
+ text representation of IPv6 addresses,
+ but reversed as in IN-ADDR.ARPA.
+ All 8 words must be present except when one set of consecutive
+ zero words is replaced with <userinput>.zz.</userinput>
+ analogous to double colons (::) in standard IPv6 text
+ encodings.
+ The IPv6 prefix length must be between 64 and 128.
+ </para>
+ </listitem>
+ </varlistentry>
- <para>
- NSDNAME triggers match names of authoritative servers
- for the query name, a parent of the query name, a CNAME for
- query name, or a parent of a CNAME.
- They are encoded as subdomains of
- <userinput>rpz-nsdomain</userinput> relativized
- to the RPZ origin name.
- 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>
+ <varlistentry>
+ <term><command>QNAME</command></term>
+ <listitem>
+ <para>
+ QNAME policy records are triggered by query names of
+ requests and targets of CNAME records resolved to generate
+ the response.
+ The owner name of a QNAME policy record is
+ the query name relativized to the policy zone.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>RPZ-IP</command></term>
+ <listitem>
+ <para>
+ IP triggers are IP addresses in an
+ A or AAAA record in the ANSWER section of a response.
+ They are encoded like client-IP triggers except as
+ subdomains of <command>rpz-ip</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>RPZ-NSDNAME</command></term>
+ <listitem>
+ <para>
+ NSDNAME triggers match names of authoritative servers
+ for the query name, a parent of the query name, a CNAME for
+ query name, or a parent of a CNAME.
+ They are encoded as subdomains of
+ <command>rpz-nsdname</command> relativized
+ to the RPZ origin name.
+ NSIP triggers match IP addresses in A and
+ AAAA RRsets for domains that can be checked against NSDNAME
+ policy records.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>RPZ-NSIP</command></term>
+ <listitem>
+ <para>
+ NSIP triggers are encoded like IP triggers except as
+ subdomains of <command>rpz-nsip</command>.
+ 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>
+ </listitem>
+ </varlistentry>
+ </variablelist>
<para>
- 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
+ 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 are rewritten according to at most one
policy record, a single record encoding an action (other than
<command>DISABLED</command> actions) must be chosen.
- Triggers or the records that encode them are chosen in
- the following order:
+ Triggers or the records that encode them are chosen for the
+ rewriting in the following order:
<itemizedlist>
<listitem>Choose the triggered record in the zone that appears
- first in the response-policy option.
+ first in the <command>response-policy</command> option.
</listitem>
- <listitem>Prefer QNAME to IP to NSDNAME to NSIP triggers
- in a single zone.
+ <listitem>Prefer CLIENT-IP to QNAME to IP to NSDNAME to NSIP
+ triggers in a single zone.
</listitem>
<listitem>Among NSDNAME triggers, prefer the
trigger that matches the smallest name under the DNSSEC ordering.
</para>
<para>
- RPZ record sets are sets of any types of DNS record except
- DNAME or DNSSEC that encode actions or responses to queries.
- <itemizedlist>
- <listitem>The <command>NXDOMAIN</command> response is encoded
- by a CNAME whose target is the root domain (.)
- </listitem>
- <listitem>A CNAME whose target is the wildcard top-level
- domain (*.) specifies the <command>NODATA</command> action,
- which rewrites the response to NODATA or ANCOUNT=1.
- </listitem>
- <listitem>The <command>Local Data</command> action is
- represented by a set ordinary DNS records that are used
- to answer queries. Queries for record types not the
- set are answered with NODATA.
-
- A special form of local data is a CNAME whose target is a
- wildcard such as *.example.com.
- It is used as if were an ordinary CNAME after the astrisk (*)
- has been replaced with the query name.
- The purpose for this special form is query logging in the
- walled garden's authority DNS server.
- </listitem>
- <listitem>The <command>PASSTHRU</command> policy is specified
- by a CNAME whose target is <command>rpz-passthru.</command>
- It causes the response to not be rewritten
- and is most often used to "poke holes" in policies for
- CIDR blocks.
- (A CNAME whose target is the variable part of its owner name
- is an obsolete specification of the PASSTHRU policy.)
- </listitem>
- </itemizedlist>
+ RPZ record sets are any types of DNS record except
+ DNAME or DNSSEC that encode actions or responses to
+ individual queries.
+ Any of the policies can be used with any of the triggers.
+ For example, while the <command>TCP-only</command> policy is
+ commonly used with <command>client-IP</command> triggers,
+ it cn be used with any type of trigger to force the use of
+ TCP for responses with owner names in a zone.
+ <variablelist>
+ <varlistentry>
+ <term><command>PASSTHRU</command></term>
+ <listitem>
+ <para>
+ The whitelist policy is specified
+ by a CNAME whose target is <command>rpz-passthru</command>.
+ It causes the response to not be rewritten
+ and is most often used to "poke holes" in policies for
+ CIDR blocks.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>DROP</command></term>
+ <listitem>
+ <para>
+ The blacklist policy is specified
+ by a CNAME whose target is <command>rpz-drop</command>.
+ It causes the response to be discarded.
+ Nothing is sent to the DNS client.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>TCP-Only</command></term>
+ <listitem>
+ <para>
+ The "slip" policy is specified
+ by a CNAME whose target is <command>rpz-tcp-only</command>.
+ It changes UDP responses to short, truncated DNS responses
+ that require the DNS client to try again with TCP.
+ It is used to mitigate distributed DNS reflection attacks.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>NXDOMAIN</command></term>
+ <listitem>
+ <para>
+ The domain undefined response is encoded
+ by a CNAME whose target is the root domain (.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>NODATA</command></term>
+ <listitem>
+ <para>
+ The empty set of resource records is specified by
+ CNAME whose target is the wildcard top-level
+ domain (*.).
+ It rewrites the response to NODATA or ANCOUNT=1.
+ </listitem>
+ </para>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>Local Data</command></term>
+ <listitem>
+ <para>
+ A set of ordinary DNS records can be used to answer queries.
+ Queries for record types not the set are answered with
+ NODATA.
+ </para>
+
+ <para>
+ A special form of local data is a CNAME whose target is a
+ wildcard such as *.example.com.
+ It is used as if were an ordinary CNAME after the astrisk (*)
+ has been replaced with the query name.
+ The purpose for this special form is query logging in the
+ walled garden's authority DNS server.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</para>
<para>
- The actions specified in a policy zone can be overridden with a
- <command>policy</command> clause in the
+ All of the actions specified in all of the individual records
+ in a policy zone
+ can be overridden with a <command>policy</command> clause in the
<command>response-policy</command> option.
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."
- </listitem>
- <listitem><command>DISABLED</command> causes policy records to do
- nothing but log what they might have done.
- The response to the DNS query will be written according to
- any triggered policy records that are not disabled.
- Disabled policy zones should appear first,
- because they will often not be logged
- if a higher precedence trigger is found first.
- </listitem>
- <listitem><command>PASSTHRU</command> causes all policy records
- to act as if they were CNAME records with targets the variable
- part of their owner name. They protect the response from
- being changed.
- </listitem>
- <listitem><command>NXDOMAIN</command> causes all RPZ records
- to specify NXDOMAIN policies.
- </listitem>
- <listitem><command>NODATA</command> overrides with the
- NODATA policy
- </listitem>
- <listitem><command>CNAME domain</command> causes all RPZ
- policy records to act as if they were "cname domain" records.
- </listitem>
- </itemizedlist>
+ <variablelist>
+ <varlistentry>
+ <term><command>GIVEN</command></term>
+ <listitem>
+ <para>The placeholder policy says "do not override but
+ perform the action specified in the zone."
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>DISABLED</command></term>
+ <listitem>
+ <para>
+ The testing override policy causes policy zone records to do
+ nothing but log what they would have done if the
+ policy zone were not disabled.
+ The response to the DNS query will be written (or not)
+ according to any triggered policy records that are not
+ disabled.
+ Disabled policy zones should appear first,
+ because they will often not be logged
+ if a higher precedence trigger is found first.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>PASSTHRU</command></term>,
+ <term><command>DROP</command></term>,
+ <term><command>TCP-Only</command></term>,
+ <term><command>NXDOMAIN</command></term>,
+ and
+ <term><command>NODATA</command></term>
+ <listitem>
+ <para>
+ override with the corresponding per-record policy.
+ </listitem>
+ </para>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>CNAME domain</command></term>
+ <listitem>
+ <para>
+ causes all RPZ policy records to act as if they were
+ "cname domain" records.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</para>
<para>
</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
+ No DNS records are needed for a QNAME or Client-IP trigger.
+ The name or IP address itself is sufficient,
+ so in principle the query name need not be recursively resolved.
+ However, not resolving the requested
+ name can leak 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
+ significant time.
+ The <command>qname-wait-recurse no</command> option
+ overrides that default behavior when recursion cannot
+ change a non-error response.
+ The option does not affect QNAME or client-IP 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.
+ The option can cause appear to rewrite error responses
+ such as SERVFAIL when no recursion is done to discover problems
+ at the authoritative server.
</para>
<para>
; QNAME policy records. There are no periods (.) after the owner names.
nxdomain.domain.com CNAME . ; NXDOMAIN policy
+*.nxdomain.domain.com CNAME . ; NXDOMAIN policy
nodata.domain.com CNAME *. ; NODATA policy
+*.nodata.domain.com CNAME *. ; NODATA policy
bad.domain.com A 10.0.0.1 ; redirect to a walled garden
AAAA 2001:2::1
+bzone.domain.com CNAME garden.example.com.
; do not rewrite (PASSTHRU) OK.DOMAIN.COM
ok.domain.com CNAME rpz-passthru.
-bzone.domain.com CNAME garden.example.com.
-
; redirect x.bzone.domain.com to x.bzone.domain.com.garden.example.com
*.bzone.domain.com CNAME *.garden.example.com.
-; IP policy records that rewrite all answers for 127/8 except 127.0.0.1
+; IP policy records that rewrite all responses containing A records in 127/8
+; except 127.0.0.1
8.0.0.0.127.rpz-ip CNAME .
32.1.0.0.127.rpz-ip CNAME rpz-passthru.
; NSDNAME and NSIP policy records
ns.domain.com.rpz-nsdname CNAME .
48.zz.2.2001.rpz-nsip CNAME .
+
+; blacklist and whitelist some DNS clients
+112.zz.2001.rpz-client-ip CNAME rpz-drop.
+8.0.0.0.127.rpz-client-ip CNAME rpz-drop.
+
+; force some DNS clients and responses in the example.com zone to TCP
+16.0.0.1.10.rpz-client-ip CNAME rpz-tcp-only.
+example.com CNAME rpz-tcp-only.
+*.example.com CNAME rpz-tcp-only.
+
</programlisting>
<para>
RPZ can affect server performance.
ISC_LANG_BEGINDECLS
#define DNS_RPZ_PREFIX "rpz-"
+/*
+ * Sub-zones of various trigger types.
+ */
+#define DNS_RPZ_CLIENT_IP_ZONE DNS_RPZ_PREFIX"client-ip"
#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"
+/*
+ * Special policies.
+ */
+#define DNS_RPZ_PASSTHRU_NAME DNS_RPZ_PREFIX"passthru"
+#define DNS_RPZ_DROP_NAME DNS_RPZ_PREFIX"drop"
+#define DNS_RPZ_TCP_ONLY_NAME DNS_RPZ_PREFIX"tcp-only"
+
typedef isc_uint8_t dns_rpz_prefix_t;
typedef enum {
DNS_RPZ_TYPE_BAD,
+ DNS_RPZ_TYPE_CLIENT_IP,
DNS_RPZ_TYPE_QNAME,
DNS_RPZ_TYPE_IP,
DNS_RPZ_TYPE_NSDNAME,
} dns_rpz_type_t;
/*
- * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_NXDOMAIN <
- * DNS_RPZ_POLICY_NODATA < DNS_RPZ_POLICY_CNAME to choose among competing
- * policies.
+ * Require DNS_RPZ_POLICY_PASSTHRU < DNS_RPZ_POLICY_DROP
+ * < DNS_RPZ_POLICY_TCP_ONLY DNS_RPZ_POLICY_NXDOMAIN < DNS_RPZ_POLICY_NODATA
+ * < DNS_RPZ_POLICY_CNAME to choose among competing policies.
*/
typedef enum {
DNS_RPZ_POLICY_GIVEN = 0, /* 'given': what policy record says */
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_CNAME = 5, /* 'cname x': answer with x's rrsets */
+ DNS_RPZ_POLICY_DROP = 3, /* 'drop': do not respond */
+ DNS_RPZ_POLICY_TCP_ONLY = 4, /* 'tcp-only': answer UDP with TC=1 */
+ DNS_RPZ_POLICY_NXDOMAIN = 5, /* 'nxdomain': answer with NXDOMAIN */
+ DNS_RPZ_POLICY_NODATA = 6, /* 'nodata': answer with ANCOUNT=0 */
+ DNS_RPZ_POLICY_CNAME = 7, /* 'cname x': answer with x's rrsets */
DNS_RPZ_POLICY_RECORD,
DNS_RPZ_POLICY_WILDCNAME,
DNS_RPZ_POLICY_MISS,
0 : (1<<((n)+1))) -1))
/*
- * A single response policy zone.
+ * The number of triggers of each type in a response policy zone.
*/
typedef struct dns_rpz_triggers dns_rpz_triggers_t;
struct dns_rpz_triggers {
+ int client_ipv4;
+ int client_ipv6;
int qname;
int ipv4;
int ipv6;
int nsipv4;
int nsipv6;
};
+/*
+ * A single response policy zone.
+ */
typedef struct dns_rpz_zone dns_rpz_zone_t;
struct dns_rpz_zone {
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 client_ip; /* DNS_RPZ_CLIENT_IP_ZONE.origin. */
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 passthru; /* DNS_RPZ_PASSTHRU_NAME. */
+ dns_name_t drop; /* DNS_RPZ_DROP_NAME. */
+ dns_name_t tcp_only; /* DNS_RPZ_TCP_ONLY_NAME. */
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;
};
/*
dns_rpz_num_t num_zones;
} p;
dns_rpz_zone_t *zones[DNS_RPZ_MAX_ZONES];
+ dns_rpz_triggers_t triggers[DNS_RPZ_MAX_ZONES];
dns_rpz_zbits_t defined;
*/
dns_rpz_zbits_t load_begun;
struct {
+ dns_rpz_zbits_t client_ipv4;
+ dns_rpz_zbits_t client_ipv6;
+ dns_rpz_zbits_t client_ip;
dns_rpz_zbits_t qname;
dns_rpz_zbits_t ipv4;
dns_rpz_zbits_t ipv6;
dns_rpz_zbits_t nsip;
dns_rpz_zbits_t qname_skip_recurse;
} have;
+ dns_rpz_triggers_t total_triggers;
isc_mem_t *mctx;
isc_refcount_t refs;
typedef struct {
unsigned int state;
# define DNS_RPZ_REWRITTEN 0x0001
-# define DNS_RPZ_DONE_QNAME 0x0002 /* qname checked */
-# define DNS_RPZ_DONE_QNAME_IP 0x0004 /* IP addresses of qname checked */
-# define DNS_RPZ_DONE_NSDNAME 0x0008 /* NS name missed; checking addresses */
-# define DNS_RPZ_DONE_IPv4 0x0010
-# define DNS_RPZ_RECURSING 0x0020
+# define DNS_RPZ_DONE_CLIENT_IP 0x0002 /* client IP address checked */
+# define DNS_RPZ_DONE_QNAME 0x0004 /* qname checked */
+# define DNS_RPZ_DONE_QNAME_IP 0x0008 /* IP addresses of qname checked */
+# define DNS_RPZ_DONE_NSDNAME 0x0010 /* NS name missed; checking addresses */
+# define DNS_RPZ_DONE_IPv4 0x0020
+# define DNS_RPZ_RECURSING 0x0040
/*
* Best match so far.
*/
- 1 - ((n) % 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.
+ * A triplet of arrays of bits flagging the existence of
+ * client-IP, IP, and NSIP 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_addr_zbits dns_rpz_addr_zbits_t;
+struct dns_rpz_addr_zbits {
+ dns_rpz_zbits_t client_ip;
+ dns_rpz_zbits_t ip;
+ dns_rpz_zbits_t nsip;
};
/*
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;
+ dns_rpz_addr_zbits_t set;
+ dns_rpz_addr_zbits_t sum;
};
/*
* 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.
*/
+/*
+ * A pair of arrays of bits flagging the existence of
+ * QNAME and NSDNAME policy triggers.
+ */
+typedef struct dns_rpz_nm_zbits dns_rpz_nm_zbits_t;
+struct dns_rpz_nm_zbits {
+ dns_rpz_zbits_t qname;
+ dns_rpz_zbits_t ns;
+};
+
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;
+ dns_rpz_nm_zbits_t set;
+ dns_rpz_nm_zbits_t wild;
};
#if 0
const char *
dns_rpz_type2str(dns_rpz_type_t type) {
switch (type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ return ("CLIENT-IP");
case DNS_RPZ_TYPE_QNAME:
return ("QNAME");
case DNS_RPZ_TYPE_IP:
dns_rpz_policy_t
dns_rpz_str2policy(const char *str) {
+ static struct {
+ const char *str;
+ dns_rpz_policy_t policy;
+ } tbl[] = {
+ {"given", DNS_RPZ_POLICY_GIVEN},
+ {"disabled", DNS_RPZ_POLICY_DISABLED},
+ {"passthru", DNS_RPZ_POLICY_PASSTHRU},
+ {"drop", DNS_RPZ_POLICY_DROP},
+ {"tcp-only", DNS_RPZ_POLICY_TCP_ONLY},
+ {"nxdomain", DNS_RPZ_POLICY_NXDOMAIN},
+ {"nodata", DNS_RPZ_POLICY_NODATA},
+ {"cname", DNS_RPZ_POLICY_CNAME},
+ {"no-op", DNS_RPZ_POLICY_PASSTHRU}, /* old passthru */
+ };
+ unsigned int n;
+
if (str == NULL)
return (DNS_RPZ_POLICY_ERROR);
- if (!strcasecmp(str, "given"))
- return (DNS_RPZ_POLICY_GIVEN);
- if (!strcasecmp(str, "disabled"))
- return (DNS_RPZ_POLICY_DISABLED);
- if (!strcasecmp(str, "passthru"))
- return (DNS_RPZ_POLICY_PASSTHRU);
- if (!strcasecmp(str, "nxdomain"))
- return (DNS_RPZ_POLICY_NXDOMAIN);
- if (!strcasecmp(str, "nodata"))
- return (DNS_RPZ_POLICY_NODATA);
- if (!strcasecmp(str, "cname"))
- return (DNS_RPZ_POLICY_CNAME);
- /*
- * Obsolete
- */
- if (!strcasecmp(str, "no-op"))
- return (DNS_RPZ_POLICY_PASSTHRU);
+ for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); ++n) {
+ if (!strcasecmp(tbl[n].str, str))
+ return (tbl[n].policy);
+ }
return (DNS_RPZ_POLICY_ERROR);
}
case DNS_RPZ_POLICY_PASSTHRU:
str = "PASSTHRU";
break;
+ case DNS_RPZ_POLICY_DROP:
+ str = "DROP";
+ break;
+ case DNS_RPZ_POLICY_TCP_ONLY:
+ str = "TCP-ONLY";
+ break;
case DNS_RPZ_POLICY_NXDOMAIN:
str = "NXDOMAIN";
break;
return (rpz_num);
}
-static inline void
-make_pair(dns_rpz_pair_zbits_t *pair, dns_rpz_zbits_t zbits,
- dns_rpz_type_t type)
+/*
+ * Make a set of bit masks given one or more bits and their type.
+ */
+static void
+make_addr_set(dns_rpz_addr_zbits_t *tgt_set, dns_rpz_zbits_t zbits,
+ dns_rpz_type_t type)
{
switch (type) {
- case DNS_RPZ_TYPE_QNAME:
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ tgt_set->client_ip = zbits;
+ tgt_set->ip = 0;
+ tgt_set->nsip = 0;
+ break;
case DNS_RPZ_TYPE_IP:
- pair->d = zbits;
- pair->ns = 0;
+ tgt_set->client_ip = 0;
+ tgt_set->ip = zbits;
+ tgt_set->nsip = 0;
break;
- case DNS_RPZ_TYPE_NSDNAME:
case DNS_RPZ_TYPE_NSIP:
- pair->d = 0;
- pair->ns = zbits;
+ tgt_set->client_ip = 0;
+ tgt_set->ip = 0;
+ tgt_set->nsip = zbits;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+}
+
+static void
+make_nm_set(dns_rpz_nm_zbits_t *tgt_set,
+ dns_rpz_num_t rpz_num, dns_rpz_type_t type)
+{
+ switch (type) {
+ case DNS_RPZ_TYPE_QNAME:
+ tgt_set->qname = DNS_RPZ_ZBIT(rpz_num);
+ tgt_set->ns = 0;
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ tgt_set->qname = 0;
+ tgt_set->ns = DNS_RPZ_ZBIT(rpz_num);
break;
default:
INSIST(0);
}
/*
- * Mark a node and all of its parents as having IP or NSIP data
+ * Mark a node and all of its parents as having client-IP, IP, or NSIP data
*/
static void
set_sum_pair(dns_rpz_cidr_node_t *cnode) {
dns_rpz_cidr_node_t *child;
- dns_rpz_pair_zbits_t sum;
+ dns_rpz_addr_zbits_t sum;
do {
- sum = cnode->pair;
+ sum = cnode->set;
child = cnode->child[0];
if (child != NULL) {
- sum.d |= child->sum.d;
- sum.ns |= child->sum.ns;
+ sum.client_ip |= child->sum.client_ip;
+ sum.ip |= child->sum.ip;
+ sum.nsip |= child->sum.nsip;
}
child = cnode->child[1];
if (child != NULL) {
- sum.d |= child->sum.d;
- sum.ns |= child->sum.ns;
+ sum.client_ip |= child->sum.client_ip;
+ sum.ip |= child->sum.ip;
+ sum.nsip |= child->sum.nsip;
}
- if (cnode->sum.d == sum.d &&
- cnode->sum.ns == sum.ns)
+ if (cnode->sum.client_ip == sum.client_ip &&
+ cnode->sum.ip == sum.ip &&
+ cnode->sum.nsip == sum.nsip)
break;
cnode->sum = sum;
cnode = cnode->parent;
}
rpzs->have.qname_skip_recurse = zbits;
+ rpzs->have.client_ip = rpzs->have.client_ipv4 | rpzs->have.client_ipv6;
rpzs->have.ip = rpzs->have.ipv4 | rpzs->have.ipv6;
rpzs->have.nsip = rpzs->have.nsipv4 | rpzs->have.nsipv6;
}
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;
- rpz = rpzs->zones[rpz_num];
switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ REQUIRE(tgt_ip != NULL);
+ if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
+ cnt = &rpzs->triggers[rpz_num].client_ipv4;
+ have = &rpzs->have.client_ipv4;
+ } else {
+ cnt = &rpzs->triggers[rpz_num].client_ipv6;
+ have = &rpzs->have.client_ipv6;
+ }
+ break;
case DNS_RPZ_TYPE_QNAME:
- cnt = &rpz->triggers.qname;
+ cnt = &rpzs->triggers[rpz_num].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;
+ cnt = &rpzs->triggers[rpz_num].ipv4;
have = &rpzs->have.ipv4;
} else {
- cnt = &rpz->triggers.ipv6;
+ cnt = &rpzs->triggers[rpz_num].ipv6;
have = &rpzs->have.ipv6;
}
break;
case DNS_RPZ_TYPE_NSDNAME:
- cnt = &rpz->triggers.nsdname;
+ cnt = &rpzs->triggers[rpz_num].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;
+ cnt = &rpzs->triggers[rpz_num].nsipv4;
have = &rpzs->have.nsipv4;
} else {
- cnt = &rpz->triggers.nsipv6;
+ cnt = &rpzs->triggers[rpz_num].nsipv6;
have = &rpzs->have.nsipv6;
}
break;
/*
* bin/tests/system/rpz/tests.sh looks for "invalid rpz".
*/
- if (level < DNS_RPZ_DEBUG_QUIET
- && isc_log_wouldlog(dns_lctx, level)) {
+ if (level < DNS_RPZ_DEBUG_QUIET &&
+ isc_log_wouldlog(dns_lctx, level)) {
dns_name_format(name, namebuf, sizeof(namebuf));
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
DNS_LOGMODULE_RBTDB, level,
return (ISC_R_FAILURE);
i = 0;
while (i < DNS_RPZ_CIDR_WORDS * 2) {
- if (w[i] != 0 || zeros
- || i >= DNS_RPZ_CIDR_WORDS * 2 - 1
- || w[i+1] != 0) {
+ if (w[i] != 0 || zeros ||
+ i >= DNS_RPZ_CIDR_WORDS * 2 - 1 ||
+ w[i+1] != 0) {
INSIST((size_t)len <= sizeof(str));
n = snprintf(&str[len], sizeof(str) - len,
".%x", w[i++]);
if (dns_name_issubdomain(name, &rpz->ip))
return (DNS_RPZ_TYPE_IP);
- /*
- * Require `./configure --enable-rpz-nsip` and nsdname
- * until consistency problems are resolved.
- */
+ if (dns_name_issubdomain(name, &rpz->client_ip))
+ return (DNS_RPZ_TYPE_CLIENT_IP);
+
#ifdef ENABLE_RPZ_NSIP
if (dns_name_issubdomain(name, &rpz->nsip))
return (DNS_RPZ_TYPE_NSIP);
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)
+ dns_rpz_addr_zbits_t *new_set)
{
dns_rpz_zone_t *rpz;
char ip_str[DNS_NAME_FORMATSIZE];
rpz = rpzs->zones[rpz_num];
REQUIRE(rpz != NULL);
- make_pair(new_pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
+ make_addr_set(new_set, DNS_RPZ_ZBIT(rpz_num), rpz_type);
ip_labels = dns_name_countlabels(src_name);
if (rpz_type == DNS_RPZ_TYPE_QNAME)
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;
*/
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);
+ memset(&new_data->set, 0, sizeof(new_data->set));
+ make_nm_set(&new_data->wild, rpz_num, rpz_type);
} else {
prefix_len = 0;
- make_pair(&new_data->pair, DNS_RPZ_ZBIT(rpz_num), rpz_type);
- new_data->wild = zero;
+ make_nm_set(&new_data->set, rpz_num, rpz_type);
+ memset(&new_data->wild, 0, sizeof(new_data->wild));
}
dns_name_init(&tmp_name, tmp_name_offsets);
static isc_result_t
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,
+ const dns_rpz_addr_zbits_t *tgt_set, 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;
+ dns_rpz_addr_zbits_t set;
int cur_num, child_num;
dns_rpz_prefix_t dbit;
isc_result_t find_result;
- pair = *tgt_pair;
+ set = *tgt_set;
find_result = ISC_R_NOTFOUND;
*found = NULL;
cur = rpzs->cidr;
else
parent->child[cur_num] = child;
child->parent = parent;
- child->pair.d |= tgt_pair->d;
- child->pair.ns |= tgt_pair->ns;
+ child->set.client_ip |= tgt_set->client_ip;
+ child->set.ip |= tgt_set->ip;
+ child->set.nsip |= tgt_set->nsip;
set_sum_pair(child);
*found = cur;
return (ISC_R_SUCCESS);
}
- if ((cur->sum.d & pair.d) == 0 &&
- (cur->sum.ns & pair.ns) == 0) {
+ if ((cur->sum.client_ip & set.client_ip) == 0 &&
+ (cur->sum.ip & set.ip) == 0 &&
+ (cur->sum.nsip & set.nsip) == 0) {
/*
* This node has no relevant data
* and is in none of the target trees.
/*
* The node's key matches the target exactly.
*/
- if ((cur->pair.d & pair.d) != 0 ||
- (cur->pair.ns & pair.ns) != 0) {
+ if ((cur->set.client_ip & set.client_ip) != 0 ||
+ (cur->set.ip & set.ip) != 0 ||
+ (cur->set.nsip & set.nsip) != 0) {
/*
* It is the answer if it has data.
*/
* The node lacked relevant data,
* but will have it now.
*/
- cur->pair.d |= tgt_pair->d;
- cur->pair.ns |= tgt_pair->ns;
+ cur->set.client_ip |= tgt_set->client_ip;
+ cur->set.ip |= tgt_set->ip;
+ cur->set.nsip |= tgt_set->nsip;
set_sum_pair(cur);
*found = cur;
find_result = ISC_R_SUCCESS;
child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1);
new_parent->child[child_num] = cur;
cur->parent = new_parent;
- new_parent->pair = *tgt_pair;
+ new_parent->set = *tgt_set;
set_sum_pair(new_parent);
*found = new_parent;
return (ISC_R_SUCCESS);
}
if (dbit == cur->prefix) {
- if ((cur->pair.d & pair.d) != 0 ||
- (cur->pair.ns & pair.ns) != 0) {
+ if ((cur->set.client_ip & set.client_ip) != 0 ||
+ (cur->set.ip & set.ip) != 0 ||
+ (cur->set.nsip & set.nsip) != 0) {
/*
* We have a partial match between of all of the
* current node but only part of the target.
*/
find_result = DNS_R_PARTIALMATCH;
*found = cur;
- pair.d = trim_zbits(pair.d, cur->pair.d);
- pair.ns = trim_zbits(pair.ns, cur->pair.ns);
+ set.client_ip = trim_zbits(set.ip,
+ cur->set.client_ip);
+ set.ip = trim_zbits(set.ip,
+ cur->set.ip);
+ set.nsip = trim_zbits(set.nsip,
+ cur->set.nsip);
}
parent = cur;
cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
new_parent->child[1-child_num] = cur;
cur->parent = new_parent;
sibling->parent = new_parent;
- sibling->pair = *tgt_pair;
+ sibling->set = *tgt_set;
set_sum_pair(sibling);
*found = sibling;
return (ISC_R_SUCCESS);
{
dns_rpz_cidr_key_t tgt_ip;
dns_rpz_prefix_t tgt_prefix;
- dns_rpz_pair_zbits_t pair;
+ dns_rpz_addr_zbits_t set;
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);
+ src_name, &tgt_ip, &tgt_prefix, &set);
/*
* Log complaints about bad owner names but let the zone load.
*/
if (result != ISC_R_SUCCESS)
return (ISC_R_SUCCESS);
- result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_TRUE, &found);
+ result = search(rpzs, &tgt_ip, tgt_prefix, &set, ISC_TRUE, &found);
if (result != ISC_R_SUCCESS) {
char namebuf[DNS_NAME_FORMATSIZE];
/*
* Do not count bits that are already present
*/
- 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 ||
+ if ((nm_data->set.qname & new_data->set.qname) != 0 ||
+ (nm_data->set.ns & new_data->set.ns) != 0 ||
+ (nm_data->wild.qname & new_data->wild.qname) != 0 ||
(nm_data->wild.ns & new_data->wild.ns) != 0) {
char namebuf[DNS_NAME_FORMATSIZE];
return (ISC_R_EXISTS);
}
- 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->set.qname |= new_data->set.qname;
+ nm_data->set.ns |= new_data->set.ns;
+ nm_data->wild.qname |= new_data->wild.qname;
nm_data->wild.ns |= new_data->wild.ns;
return (ISC_R_SUCCESS);
}
if (dns_name_dynamic(&rpz->origin))
dns_name_free(&rpz->origin, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->client_ip))
+ dns_name_free(&rpz->client_ip, 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->nsip, rpzs->mctx);
if (dns_name_dynamic(&rpz->passthru))
dns_name_free(&rpz->passthru, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->drop))
+ dns_name_free(&rpz->drop, rpzs->mctx);
+ if (dns_name_dynamic(&rpz->tcp_only))
+ dns_name_free(&rpz->tcp_only, rpzs->mctx);
if (dns_name_dynamic(&rpz->cname))
dns_name_free(&rpz->cname, rpzs->mctx);
LOCK(&rpzs->maint_lock);
LOCK(&rpzs->search_lock);
if ((rpzs->load_begun & tgt) == 0) {
+ /*
+ * There is no existing version of the target zone.
+ */
rpzs->load_begun |= tgt;
dns_rpz_attach_rpzs(rpzs, load_rpzsp);
UNLOCK(&rpzs->search_lock);
return (result);
load_rpzs = *load_rpzsp;
load_rpzs->p.num_zones = rpzs->p.num_zones;
+ load_rpzs->total_triggers = rpzs->total_triggers;
+ memcpy(load_rpzs->triggers, rpzs->triggers,
+ sizeof(load_rpzs->triggers));
+ memset(&load_rpzs->triggers[rpz_num], 0,
+ sizeof(load_rpzs->triggers[rpz_num]));
load_rpzs->zones[rpz_num] = rpz;
isc_refcount_increment(&rpz->refs, NULL);
}
}
static void
-fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_triggers_t *totals) {
- dns_rpz_num_t rpz_num;
+fix_triggers(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
+ dns_rpz_num_t n;
const dns_rpz_zone_t *rpz;
+ dns_rpz_triggers_t old_totals;
dns_rpz_zbits_t zbit;
+ char namebuf[DNS_NAME_FORMATSIZE];
-# define SET_TRIG(type) \
- if (rpz == NULL || rpz->triggers.type == 0) { \
+# define SET_TRIG(n, zbit, type) \
+ if (rpzs->triggers[n].type == 0) { \
rpzs->have.type &= ~zbit; \
} else { \
- totals->type += rpz->triggers.type; \
+ rpzs->total_triggers.type += rpzs->triggers[n].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);
+ memcpy(&old_totals, &rpzs->total_triggers, sizeof(old_totals));
+ memset(&rpzs->total_triggers, 0, sizeof(rpzs->total_triggers));
+ for (n = 0; n < rpzs->p.num_zones; ++n) {
+ rpz = rpzs->zones[n];
+ zbit = DNS_RPZ_ZBIT(n);
+ SET_TRIG(n, zbit, client_ipv4);
+ SET_TRIG(n, zbit, client_ipv6);
+ SET_TRIG(n, zbit, qname);
+ SET_TRIG(n, zbit, ipv4);
+ SET_TRIG(n, zbit, ipv6);
+ SET_TRIG(n, zbit, nsdname);
+ SET_TRIG(n, zbit, nsipv4);
+ SET_TRIG(n, zbit, nsipv6);
}
fix_qname_skip_recurse(rpzs);
+ dns_name_format(&rpzs->zones[rpz_num]->origin,
+ namebuf, sizeof(namebuf));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_INFO_LEVEL,
+ "(re)loading 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, rpzs->total_triggers.qname,
+ old_totals.nsdname, rpzs->total_triggers.nsdname,
+ old_totals.ipv4 + old_totals.ipv6,
+ rpzs->total_triggers.ipv4 + rpzs->total_triggers.ipv6,
+ old_totals.nsipv4 + old_totals.nsipv6,
+ rpzs->total_triggers.nsipv4 + rpzs->total_triggers.nsipv6);
+
# undef SET_TRIG
}
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_rpz_zbits_t new_bit;
+ dns_rpz_addr_zbits_t new_ip;
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;
* This is a successful initial zone loading,
* perhaps for a new instance of a view.
*/
- fix_triggers(rpzs, &new_totals);
+ fix_triggers(rpzs, rpz_num);
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->search_lock);
/*
- * Copy the other policy zones to the new summary databases
- * unless there is only one policy zone.
+ * Unless there is only one policy zone, copy the other policy zones
+ * from the old policy structure to the new summary databases.
*/
if (rpzs->p.num_zones > 1) {
+ new_bit = ~DNS_RPZ_ZBIT(rpz_num);
+
/*
* 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) {
+ new_ip.ip = cnode->set.ip & new_bit;
+ new_ip.client_ip = cnode->set.client_ip & new_bit;
+ new_ip.nsip = cnode->set.nsip & new_bit;
+ if (new_ip.client_ip != 0 ||
+ new_ip.ip != 0 ||
+ new_ip.nsip != 0) {
result = search(load_rpzs,
&cnode->ip, cnode->prefix,
- &new_pair, ISC_TRUE,
- &found);
+ &new_ip, ISC_TRUE, &found);
if (result == ISC_R_NOMEMORY)
goto unlock_and_detach;
INSIST(result == ISC_R_SUCCESS);
continue;
/*
* Go up until we find a branch to the right where
- * we previously took the branck to the left.
+ * we previously took the branch to the left.
*/
for (;;) {
parent_cnode = cnode->parent;
}
/*
- * Copy to the summary rbt.
+ * Copy to the summary RBT.
*/
dns_fixedname_init(&namef);
name = dns_fixedname_name(&namef);
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.set.qname = (nm_data->set.qname &
+ new_bit);
+ new_data.set.ns = nm_data->set.ns & new_bit;
+ new_data.wild.qname = (nm_data->wild.qname &
+ new_bit);
+ new_data.wild.ns = nm_data->wild.ns & new_bit;
+ if (new_data.set.qname != 0 ||
+ new_data.set.ns != 0 ||
+ new_data.wild.qname != 0 ||
new_data.wild.ns != 0) {
result = dns_name_concatenate(label,
origin, name, NULL);
}
}
- 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);
+ fix_triggers(load_rpzs, rpz_num);
/*
* Exchange the summary databases.
rpzs->rbt = load_rpzs->rbt;
load_rpzs->rbt = rbt;
+ rpzs->total_triggers = load_rpzs->total_triggers;
+
UNLOCK(&rpzs->search_lock);
+
result = ISC_R_SUCCESS;
unlock_and_detach:
case DNS_RPZ_TYPE_NSDNAME:
result = add_name(rpzs, rpz_num, rpz_type, src_name);
break;
+ case DNS_RPZ_TYPE_CLIENT_IP:
case DNS_RPZ_TYPE_IP:
case DNS_RPZ_TYPE_NSIP:
result = add_cidr(rpzs, rpz_num, rpz_type, 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_addr_zbits_t tgt_set;
dns_rpz_cidr_node_t *tgt, *parent, *child;
/*
* 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);
+ src_name, &tgt_ip, &tgt_prefix, &tgt_set);
if (result != ISC_R_SUCCESS)
return;
- result = search(rpzs, &tgt_ip, tgt_prefix, &pair, ISC_FALSE, &tgt);
+ result = search(rpzs, &tgt_ip, tgt_prefix, &tgt_set, ISC_FALSE, &tgt);
if (result != ISC_R_SUCCESS) {
INSIST(result == ISC_R_NOTFOUND ||
result == DNS_R_PARTIALMATCH);
* 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;
+ tgt_set.client_ip &= tgt->set.client_ip;
+ tgt_set.ip &= tgt->set.ip;
+ tgt_set.nsip &= tgt->set.nsip;
+ tgt->set.client_ip &= ~tgt_set.client_ip;
+ tgt->set.ip &= ~tgt_set.ip;
+ tgt->set.nsip &= ~tgt_set.nsip;
set_sum_pair(tgt);
adj_trigger_cnt(rpzs, rpz_num, rpz_type, &tgt_ip, tgt_prefix, ISC_FALSE);
} else {
child = tgt->child[1];
}
- if (tgt->pair.d + tgt->pair.ns != 0)
+ if (tgt->set.client_ip != 0 ||
+ tgt->set.ip != 0 ||
+ tgt->set.nsip != 0)
break;
/*
* 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.set.qname &= nm_data->set.qname;
+ del_data.set.ns &= nm_data->set.ns;
+ del_data.wild.qname &= nm_data->wild.qname;
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->set.qname &= ~del_data.set.qname;
+ nm_data->set.ns &= ~del_data.set.ns;
+ nm_data->wild.qname &= ~del_data.wild.qname;
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) {
+ if (nm_data->set.qname == 0 && nm_data->set.ns == 0 &&
+ nm_data->wild.qname == 0 && nm_data->wild.ns == 0) {
result = dns_rbt_deletenode(rpzs->rbt, nmnode, ISC_FALSE);
if (result != ISC_R_SUCCESS) {
/*
case DNS_RPZ_TYPE_NSDNAME:
del_name(rpzs, rpz_num, rpz_type, src_name);
break;
+ case DNS_RPZ_TYPE_CLIENT_IP:
case DNS_RPZ_TYPE_IP:
case DNS_RPZ_TYPE_NSIP:
del_cidr(rpzs, rpz_num, rpz_type, src_name);
dns_name_t *ip_name, dns_rpz_prefix_t *prefixp)
{
dns_rpz_cidr_key_t tgt_ip;
- dns_rpz_pair_zbits_t pair;
+ dns_rpz_addr_zbits_t tgt_set;
dns_rpz_cidr_node_t *found;
isc_result_t result;
dns_rpz_num_t rpz_num;
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)
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits &= rpzs->have.client_ipv4;
+ break;
+ case DNS_RPZ_TYPE_IP:
zbits &= rpzs->have.ipv4;
- else
+ break;
+ case DNS_RPZ_TYPE_NSIP:
zbits &= rpzs->have.nsipv4;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
} else if (netaddr->family == AF_INET6) {
dns_rpz_cidr_key_t src_ip6;
for (i = 0; i < 4; i++) {
tgt_ip.w[i] = ntohl(src_ip6.w[i]);
}
- if (rpz_type == DNS_RPZ_TYPE_IP)
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits &= rpzs->have.client_ipv6;
+ break;
+ case DNS_RPZ_TYPE_IP:
zbits &= rpzs->have.ipv6;
- else
+ break;
+ case DNS_RPZ_TYPE_NSIP:
zbits &= rpzs->have.nsipv6;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
} else {
return (DNS_RPZ_INVALID_NUM);
}
if (zbits == 0)
return (DNS_RPZ_INVALID_NUM);
- make_pair(&pair, zbits, rpz_type);
+ make_addr_set(&tgt_set, zbits, rpz_type);
LOCK(&rpzs->search_lock);
- result = search(rpzs, &tgt_ip, 128, &pair, ISC_FALSE, &found);
+ result = search(rpzs, &tgt_ip, 128, &tgt_set, ISC_FALSE, &found);
if (result == ISC_R_NOTFOUND) {
/*
* There are no eligible zones for this IP address.
* 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);
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ rpz_num = zbit_to_num(found->set.client_ip & tgt_set.client_ip);
+ break;
+ case DNS_RPZ_TYPE_IP:
+ rpz_num = zbit_to_num(found->set.ip & tgt_set.ip);
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ rpz_num = zbit_to_num(found->set.nsip & tgt_set.nsip);
+ break;
+ default:
+ INSIST(0);
+ break;
}
result = ip2name(&found->ip, found->prefix, dns_rootname, ip_name);
UNLOCK(&rpzs->search_lock);
nm_data = nmnode->data;
if (nm_data != NULL) {
if (rpz_type == DNS_RPZ_TYPE_QNAME)
- found_zbits = nm_data->pair.d;
+ found_zbits = nm_data->set.qname;
else
- found_zbits = nm_data->pair.ns;
+ found_zbits = nm_data->set.ns;
}
nmnode = nmnode->parent;
/* fall thru */
nm_data = nmnode->data;
if (nm_data != NULL) {
if (rpz_type == DNS_RPZ_TYPE_QNAME)
- found_zbits |= nm_data->wild.d;
+ found_zbits |= nm_data->wild.qname;
else
found_zbits |= nm_data->wild.ns;
}
}
/*
- * CNAME PASSTHRU. means "do not rewrite.
+ * CNAME rpz-tcp-only. means "send truncated UDP responses."
+ */
+ if (dns_name_equal(&cname.cname, &rpz->tcp_only))
+ return (DNS_RPZ_POLICY_TCP_ONLY);
+
+ /*
+ * CNAME rpz-drop. means "do not respond."
+ */
+ if (dns_name_equal(&cname.cname, &rpz->drop))
+ return (DNS_RPZ_POLICY_DROP);
+
+ /*
+ * CNAME rpz-passthru. means "do not rewrite."
*/
if (dns_name_equal(&cname.cname, &rpz->passthru))
return (DNS_RPZ_POLICY_PASSTHRU);
/*%
* response-policy {
- * zone <string> [ policy (given|disabled|passthru|
+ * zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
* nxdomain|nodata|cname <domain> ) ]
* [ recursive-only yes|no ] [ max-policy-ttl number ] ;
* } [ recursive-only yes|no ] [ max-policy-ttl number ] ;
/*
* Parse
- * given|disabled|passthru|nxdomain|nodata|cname <domain>
+ * given|disabled|passthru|drop|tcp-only|nxdomain|nodata|cname <domain>
*/
static isc_result_t
cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
doc_keyvalue, &cfg_rep_string,
&zone_kw
};
+/*
+ * "no-op" is an obsolete equivalent of "passthru".
+ */
static const char *rpz_policies[] = {
- "given", "disabled", "passthru", "no-op", "nxdomain", "nodata",
- "cname", NULL
+ "given", "disabled", "passthru", "no-op", "drop", "tcp-only",
+ "nxdomain", "nodata", "cname", NULL
};
static cfg_type_t cfg_type_rpz_policy_name = {
"policy name", cfg_parse_enum, cfg_print_ustring,