Marcus Meissner
7f803cee73
http://ss.vix.su/~vjs/rrlrpz.html OBS-URL: https://build.opensuse.org/package/show/network/bind?expand=0&rev=118
10453 lines
317 KiB
Diff
10453 lines
317 KiB
Diff
diff -r -u bin/named/client.c-orig bin/named/client.c
|
|
--- bin/named/client.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/client.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -994,6 +994,11 @@
|
|
}
|
|
if (result != ISC_R_SUCCESS)
|
|
goto done;
|
|
+ /*
|
|
+ * Stop after the question if TC was set for rate limiting.
|
|
+ */
|
|
+ if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
|
|
+ goto renderend;
|
|
result = dns_message_rendersection(client->message,
|
|
DNS_SECTION_ANSWER,
|
|
DNS_MESSAGERENDER_PARTIAL |
|
|
@@ -1134,6 +1139,51 @@
|
|
#endif
|
|
|
|
/*
|
|
+ * Try to rate limit error responses.
|
|
+ */
|
|
+ if (client->view != NULL && client->view->rrl != NULL) {
|
|
+ isc_boolean_t wouldlog;
|
|
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
|
|
+ dns_rrl_result_t rrl_result;
|
|
+
|
|
+ INSIST(rcode != dns_rcode_noerror &&
|
|
+ rcode != dns_rcode_nxdomain);
|
|
+ wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
|
|
+ rrl_result = dns_rrl(client->view, &client->peeraddr,
|
|
+ TCP_CLIENT(client),
|
|
+ dns_rdataclass_in, dns_rdatatype_none,
|
|
+ NULL, result, client->now,
|
|
+ wouldlog, log_buf, sizeof(log_buf));
|
|
+ if (rrl_result != DNS_RRL_RESULT_OK) {
|
|
+ /*
|
|
+ * Log dropped errors in the query category
|
|
+ * so that they are not lost in silence.
|
|
+ * Starts of rate-limited bursts are logged in
|
|
+ * NS_LOGCATEGORY_RRL.
|
|
+ */
|
|
+ if (wouldlog) {
|
|
+ ns_client_log(client,
|
|
+ NS_LOGCATEGORY_QUERY_EERRORS,
|
|
+ NS_LOGMODULE_CLIENT,
|
|
+ DNS_RRL_LOG_DROP,
|
|
+ "%s", log_buf);
|
|
+ }
|
|
+ /*
|
|
+ * Some error responses cannot be 'slipped',
|
|
+ * so don't try to slip any error responses.
|
|
+ */
|
|
+ if (!client->view->rrl->log_only) {
|
|
+ isc_stats_increment(ns_g_server->nsstats,
|
|
+ dns_nsstatscounter_ratedropped);
|
|
+ isc_stats_increment(ns_g_server->nsstats,
|
|
+ dns_nsstatscounter_dropped);
|
|
+ ns_client_next(client, DNS_R_DROP);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
* Message may be an in-progress reply that we had trouble
|
|
* with, in which case QR will be set. We need to clear QR before
|
|
* calling dns_message_reply() to avoid triggering an assertion.
|
|
diff -r -u bin/named/config.c-orig bin/named/config.c
|
|
--- bin/named/config.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/config.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -228,6 +228,13 @@
|
|
notify no;\n\
|
|
allow-new-zones no;\n\
|
|
\n\
|
|
+ # Prevent use of this zone in DNS amplified reflection DoS attacks\n\
|
|
+ rate-limit {\n\
|
|
+ responses-per-second 3;\n\
|
|
+ slip 0;\n\
|
|
+ min-table-size 10;\n\
|
|
+ };\n\
|
|
+\n\
|
|
zone \"version.bind\" chaos {\n\
|
|
type master;\n\
|
|
database \"_builtin version\";\n\
|
|
diff -r -u bin/named/include/named/query.h-orig bin/named/include/named/query.h
|
|
--- bin/named/include/named/query.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/include/named/query.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -85,6 +85,7 @@
|
|
#define NS_QUERYATTR_CACHEACLOK 0x2000
|
|
#define NS_QUERYATTR_DNS64 0x4000
|
|
#define NS_QUERYATTR_DNS64EXCLUDE 0x8000
|
|
+#define NS_QUERYATTR_RRL_CHECKED 0x10000
|
|
|
|
|
|
isc_result_t
|
|
diff -r -u bin/named/include/named/server.h-orig bin/named/include/named/server.h
|
|
--- bin/named/include/named/server.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/include/named/server.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -167,7 +167,10 @@
|
|
|
|
dns_nsstatscounter_rpz_rewrites = 36,
|
|
|
|
- dns_nsstatscounter_max = 37
|
|
+ dns_nsstatscounter_ratedropped = 37,
|
|
+ dns_nsstatscounter_rateslipped = 38,
|
|
+
|
|
+ dns_nsstatscounter_max = 39
|
|
};
|
|
|
|
void
|
|
diff -r -u bin/named/query.c-orig bin/named/query.c
|
|
--- bin/named/query.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/query.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -193,7 +193,7 @@
|
|
#ifdef NEWSTATS
|
|
/* Do query type statistics
|
|
*
|
|
- * We only increment per-type if we're using the authoriative
|
|
+ * We only increment per-type if we're using the authoritative
|
|
* answer counter, preventing double-counting.
|
|
*/
|
|
if (counter == dns_nsstatscounter_authans) {
|
|
@@ -879,11 +879,11 @@
|
|
static void
|
|
rpz_log_rewrite(ns_client_t *client, isc_boolean_t disabled,
|
|
dns_rpz_policy_t policy, dns_rpz_type_t type,
|
|
- dns_zone_t *zone, 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.
|
|
@@ -893,8 +893,8 @@
|
|
isc_stats_increment(ns_g_server->nsstats,
|
|
dns_nsstatscounter_rpz_rewrites);
|
|
}
|
|
- if (zone != NULL) {
|
|
- zonestats = dns_zone_getrequeststats(zone);
|
|
+ if (p_zone != NULL) {
|
|
+ zonestats = dns_zone_getrequeststats(p_zone);
|
|
if (zonestats != NULL)
|
|
isc_stats_increment(zonestats,
|
|
dns_nsstatscounter_rpz_rewrites);
|
|
@@ -904,22 +904,21 @@
|
|
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 " : "",
|
|
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;
|
|
@@ -927,44 +926,44 @@
|
|
/*
|
|
* bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
|
|
*/
|
|
- dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
|
|
- dns_name_format(name, 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, 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);
|
|
}
|
|
@@ -3913,7 +3912,7 @@
|
|
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);
|
|
@@ -3921,16 +3920,16 @@
|
|
}
|
|
|
|
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);
|
|
}
|
|
@@ -3959,13 +3958,83 @@
|
|
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_CLIENT_IP:
|
|
+ zbits = rpzs->have.client_ip;
|
|
+ break;
|
|
+ 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;
|
|
@@ -3977,15 +4046,13 @@
|
|
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;
|
|
@@ -3995,16 +4062,15 @@
|
|
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);
|
|
@@ -4019,9 +4085,8 @@
|
|
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);
|
|
@@ -4034,6 +4099,8 @@
|
|
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);
|
|
@@ -4072,177 +4139,97 @@
|
|
}
|
|
|
|
/*
|
|
- * 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;
|
|
+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;
|
|
|
|
- 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)
|
|
-{
|
|
- isc_result_t result;
|
|
-
|
|
- result = rpz_rrset_find(client, rpz_type, name, type, dbp, version,
|
|
- rdatasetp, resuming);
|
|
- switch (result) {
|
|
- case ISC_R_SUCCESS:
|
|
- case DNS_R_GLUE:
|
|
- case DNS_R_ZONECUT:
|
|
- 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_CLIENT_IP:
|
|
+ suffix = &rpz->client_ip;
|
|
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_QNAME:
|
|
+ suffix = &rpz->origin;
|
|
break;
|
|
- case DNS_R_DELEGATION:
|
|
- case DNS_R_DUPLICATE:
|
|
- case DNS_R_DROP:
|
|
+ case DNS_RPZ_TYPE_IP:
|
|
+ suffix = &rpz->ip;
|
|
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_NSDNAME:
|
|
+ suffix = &rpz->nsdname;
|
|
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);
|
|
- }
|
|
+ case DNS_RPZ_TYPE_NSIP:
|
|
+ suffix = &rpz->nsip;
|
|
break;
|
|
+ default:
|
|
+ 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;
|
|
- }
|
|
- 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);
|
|
+ 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 (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;
|
|
@@ -4250,31 +4237,28 @@
|
|
|
|
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;
|
|
|
|
@@ -4282,10 +4266,8 @@
|
|
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);
|
|
@@ -4301,9 +4283,8 @@
|
|
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);
|
|
}
|
|
/*
|
|
@@ -4318,7 +4299,7 @@
|
|
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);
|
|
@@ -4327,162 +4308,477 @@
|
|
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:
|
|
- policy = DNS_RPZ_POLICY_NODATA;
|
|
- break;
|
|
+ *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().
|
|
- */
|
|
- dns_rdataset_disassociate(*rdatasetp);
|
|
- dns_db_detachnode(*dbp, nodep);
|
|
- /*
|
|
- * Fall through to treat it as a miss.
|
|
+ * 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_NXDOMAIN:
|
|
case DNS_R_EMPTYNAME:
|
|
- /*
|
|
- * If we don't get a qname hit,
|
|
- * see if it is worth looking for other types.
|
|
- */
|
|
- (void)dns_db_rpz_enabled(*dbp, client->query.rpz_st);
|
|
- dns_db_detach(dbp);
|
|
- dns_zone_detach(zonep);
|
|
- result = DNS_R_NXDOMAIN;
|
|
- policy = DNS_RPZ_POLICY_MISS;
|
|
- break;
|
|
+ return (DNS_R_NXDOMAIN);
|
|
default:
|
|
- dns_db_detach(dbp);
|
|
- dns_zone_detach(zonep);
|
|
- rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
|
|
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
|
|
"", result);
|
|
return (DNS_R_SERVFAIL);
|
|
}
|
|
+}
|
|
|
|
- *policyp = policy;
|
|
- return (result);
|
|
+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)) {
|
|
+ /*
|
|
+ * Save the replacement rdataset from the policy
|
|
+ * and make the previous replacement rdataset scratch.
|
|
+ */
|
|
+ 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;
|
|
}
|
|
|
|
/*
|
|
- * Build and look for a QNAME or NSDNAME owner name in a response policy zone.
|
|
+ * Check this address in every eligible policy zone.
|
|
*/
|
|
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(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_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_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;
|
|
- unsigned int labels;
|
|
isc_result_t result;
|
|
|
|
- st = client->query.rpz_st;
|
|
- zone = NULL;
|
|
- db = NULL;
|
|
- node = NULL;
|
|
+ dns_fixedname_init(&ip_namef);
|
|
+ ip_name = dns_fixedname_name(&ip_namef);
|
|
|
|
- for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
|
|
- rpz != NULL;
|
|
- rpz = ISC_LIST_NEXT(rpz, link)) {
|
|
- if (!RECURSIONOK(client) && rpz->recursive_only)
|
|
- continue;
|
|
+ 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 check policy zones that cannot replace a policy
|
|
- * already known to match.
|
|
+ * 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)
|
|
- continue;
|
|
+ (st->m.type < rpz_type ||
|
|
+ st->m.prefix > prefix))
|
|
+ break;
|
|
}
|
|
+
|
|
/*
|
|
- * Construct the policy's owner name.
|
|
+ * Get the policy for a prefix at least as long
|
|
+ * as the prefix of the entry we had before.
|
|
*/
|
|
- 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);
|
|
+ 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:
|
|
/*
|
|
- * Trim the name until it is not too long.
|
|
+ * 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.
|
|
*/
|
|
- 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);
|
|
+ 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,
|
|
+ * 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.
|
|
+ * 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 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,
|
|
+ policy, p_name, prefix, result,
|
|
+ &p_zone, &p_db, &p_node,
|
|
+ p_rdatasetp, p_version);
|
|
+ break;
|
|
}
|
|
- dns_name_split(prefix, labels - 1, NULL, prefix);
|
|
+
|
|
+ /*
|
|
+ * 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;
|
|
+ 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);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * 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);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Look for IP addresses in A and AAAA rdatasets
|
|
+ * that trigger all eligible IP or NSIP policy rules.
|
|
+ */
|
|
+static isc_result_t
|
|
+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_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;
|
|
+ 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;
|
|
+
|
|
+ /*
|
|
+ * 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;
|
|
}
|
|
|
|
/*
|
|
- * See if the policy record exists and get its policy.
|
|
+ * Do not check policy zones that cannot replace a previously
|
|
+ * found policy.
|
|
*/
|
|
- result = rpz_find(client, qtype, rpz_qname, qname, rpz,
|
|
- rpz_type, &zone, &db, &version, &node,
|
|
- rdatasetp, &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)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Get the next policy zone's record for this trigger name.
|
|
+ */
|
|
+ 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:
|
|
- 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,
|
|
+ * client-IP over 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
|
|
/*
|
|
@@ -4505,11 +4801,12 @@
|
|
* names in TLDs that start with "rpz-" should
|
|
* ICANN ever allow such TLDs.
|
|
*/
|
|
- labels = dns_name_countlabels(qname);
|
|
+ unsigned int labels;
|
|
+ labels = dns_name_countlabels(trig_name);
|
|
if (labels >= 2) {
|
|
dns_label_t label;
|
|
|
|
- dns_name_getlabel(qname, labels-2, &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,
|
|
@@ -4517,46 +4814,29 @@
|
|
continue;
|
|
}
|
|
#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);
|
|
+ }
|
|
/*
|
|
- * Merely log DNS_RPZ_POLICY_DISABLED hits.
|
|
+ * Log DNS_RPZ_POLICY_DISABLED zones
|
|
+ * and try the next eligible policy zone.
|
|
*/
|
|
- if (rpz->policy == DNS_RPZ_POLICY_DISABLED) {
|
|
- rpz_log_rewrite(client, ISC_TRUE, policy,
|
|
- rpz_type, zone, rpz_qname);
|
|
- 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);
|
|
- }
|
|
- st->m.node = node;
|
|
- node = NULL;
|
|
- st->m.db = db;
|
|
- db = NULL;
|
|
- st->m.version = version;
|
|
- st->m.zone = zone;
|
|
- zone = NULL;
|
|
+ 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);
|
|
}
|
|
|
|
@@ -4569,7 +4849,7 @@
|
|
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))
|
|
@@ -4589,7 +4869,9 @@
|
|
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_netaddr_t netaddr;
|
|
isc_result_t result;
|
|
|
|
st = client->query.rpz_st;
|
|
@@ -4603,10 +4885,10 @@
|
|
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;
|
|
@@ -4619,7 +4901,7 @@
|
|
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:
|
|
@@ -4629,73 +4911,136 @@
|
|
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;
|
|
+
|
|
+ /*
|
|
+ * Check triggers for the client IP address once.
|
|
+ */
|
|
+ if ((st->state & DNS_RPZ_DONE_CLIENT_IP) == 0) {
|
|
+ st->state |= DNS_RPZ_DONE_CLIENT_IP;
|
|
+ zbits = rpz_get_zbits(client, dns_rdatatype_none,
|
|
+ DNS_RPZ_TYPE_CLIENT_IP);
|
|
+ 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) {
|
|
+ if (qresult_type == 2) {
|
|
+ /*
|
|
+ * This request needs recursion that has not been done.
|
|
+ * Get bits for the policy zones that do not
|
|
+ * require recursion be done before applying their
|
|
+ * policies. By not recursing after applying their
|
|
+ * policies, those policy zones leak information
|
|
+ * to operators of the targeted zones.
|
|
+ * Bail out to wait for recursion if there are no
|
|
+ * such policies.
|
|
+ */
|
|
+ zbits = client->view->rpzs->have.qname_skip_recurse;
|
|
+ if (zbits == 0) {
|
|
+ result = ISC_R_SUCCESS;
|
|
+ goto cleanup;
|
|
+ }
|
|
+ } 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;
|
|
+
|
|
/*
|
|
- * Check rules for the query name if this is the first time
|
|
- * for the current qname, i.e. we've not been recursing.
|
|
- * There is a first time for each name in a CNAME chain.
|
|
+ * This may have been an attempt to find a qname trigger
|
|
+ * before required recursion (qresult_type == 2).
|
|
+ * If so and no pre-recursion triggers hit, then 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.
|
|
*/
|
|
- result = rpz_rewrite_name(client, qtype, client->query.qname,
|
|
- DNS_RPZ_TYPE_QNAME, &rdataset);
|
|
- if (result != ISC_R_SUCCESS)
|
|
+ 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 > client->view->rpz_min_ns_labels) {
|
|
+ while (st->r.label > client->view->rpzs->p.min_ns_labels) {
|
|
/*
|
|
* Get NS rrset for each domain in the current qname.
|
|
*/
|
|
@@ -4709,8 +5054,8 @@
|
|
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)
|
|
@@ -4765,7 +5110,7 @@
|
|
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;
|
|
@@ -4782,11 +5127,11 @@
|
|
* 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);
|
|
@@ -4797,9 +5142,9 @@
|
|
/*
|
|
* 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;
|
|
@@ -4809,10 +5154,16 @@
|
|
} 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;
|
|
|
|
@@ -4827,7 +5178,7 @@
|
|
if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU &&
|
|
result != DNS_R_DELEGATION)
|
|
rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
|
|
- st->m.type, st->m.zone, st->qname);
|
|
+ st->m.type, st->m.zone, st->p_name);
|
|
rpz_match_clear(st);
|
|
}
|
|
if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
|
|
@@ -4846,19 +5197,25 @@
|
|
* 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))
|
|
@@ -4938,7 +5295,7 @@
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
rpz_log_rewrite(client, ISC_FALSE, st->m.policy,
|
|
- st->m.type, st->m.zone, st->qname);
|
|
+ st->m.type, st->m.zone, st->p_name);
|
|
ns_client_qnamereplace(client, fname);
|
|
/*
|
|
* Turn off DNSSEC because the results of a
|
|
@@ -5865,8 +6222,130 @@
|
|
resume:
|
|
CTRACE("query_find: resume");
|
|
|
|
- if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
|
|
- (RECURSIONOK(client) || !client->view->rpz_recursive_only) &&
|
|
+ /*
|
|
+ * Rate limit these responses to this client.
|
|
+ * Do not delay counting and handling obvious referrals,
|
|
+ * since those won't come here again.
|
|
+ * Delay handling delegations for which we are certain to recurse and
|
|
+ * return here (DNS_R_DELEGATION, not a child of one of our
|
|
+ * own zones, and recursion enabled)
|
|
+ * Count each response at most once.
|
|
+ */
|
|
+ if (client->view->rrl != NULL &&
|
|
+ ((fname != NULL && dns_name_isabsolute(fname)) ||
|
|
+ (result == ISC_R_NOTFOUND && !RECURSIONOK(client))) &&
|
|
+ !(result == DNS_R_DELEGATION && !is_zone && RECURSIONOK(client)) &&
|
|
+ (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0) {
|
|
+ dns_rdataset_t nc_rdataset;
|
|
+ isc_boolean_t wouldlog;
|
|
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
|
|
+ isc_result_t nc_result, resp_result;
|
|
+ dns_rrl_result_t rrl_result;
|
|
+
|
|
+ client->query.attributes |= NS_QUERYATTR_RRL_CHECKED;
|
|
+
|
|
+ wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
|
|
+ tname = fname;
|
|
+ if (result == DNS_R_NXDOMAIN) {
|
|
+ /*
|
|
+ * Use the database origin name to rate limit NXDOMAIN
|
|
+ */
|
|
+ if (db != NULL)
|
|
+ tname = dns_db_origin(db);
|
|
+ resp_result = result;
|
|
+ } else if (result == DNS_R_NCACHENXDOMAIN &&
|
|
+ rdataset != NULL &&
|
|
+ dns_rdataset_isassociated(rdataset) &&
|
|
+ (rdataset->attributes &
|
|
+ DNS_RDATASETATTR_NEGATIVE) != 0) {
|
|
+ /*
|
|
+ * Try to use owner name in the negative cache SOA.
|
|
+ */
|
|
+ dns_fixedname_init(&fixed);
|
|
+ dns_rdataset_init(&nc_rdataset);
|
|
+ for (nc_result = dns_rdataset_first(rdataset);
|
|
+ nc_result == ISC_R_SUCCESS;
|
|
+ nc_result = dns_rdataset_next(rdataset)) {
|
|
+ dns_ncache_current(rdataset,
|
|
+ dns_fixedname_name(&fixed),
|
|
+ &nc_rdataset);
|
|
+ if (nc_rdataset.type == dns_rdatatype_soa) {
|
|
+ dns_rdataset_disassociate(&nc_rdataset);
|
|
+ tname = dns_fixedname_name(&fixed);
|
|
+ break;
|
|
+ }
|
|
+ dns_rdataset_disassociate(&nc_rdataset);
|
|
+ }
|
|
+ resp_result = DNS_R_NXDOMAIN;
|
|
+ } else if (result == DNS_R_NXRRSET ||
|
|
+ result == DNS_R_EMPTYNAME) {
|
|
+ resp_result = DNS_R_NXRRSET;
|
|
+ } else if (result == DNS_R_DELEGATION) {
|
|
+ resp_result = result;
|
|
+ } else if (result == ISC_R_NOTFOUND) {
|
|
+ /*
|
|
+ * Handle referral to ".", including when recursion
|
|
+ * is off or not requested and the hints have not
|
|
+ * been loaded or we have "additional-from-cache no".
|
|
+ */
|
|
+ tname = dns_rootname;
|
|
+ resp_result = DNS_R_DELEGATION;
|
|
+ } else {
|
|
+ resp_result = ISC_R_SUCCESS;
|
|
+ }
|
|
+ rrl_result = dns_rrl(client->view, &client->peeraddr,
|
|
+ ISC_TF((client->attributes
|
|
+ & NS_CLIENTATTR_TCP) != 0),
|
|
+ client->message->rdclass, qtype, tname,
|
|
+ resp_result, client->now,
|
|
+ wouldlog, log_buf, sizeof(log_buf));
|
|
+ if (rrl_result != DNS_RRL_RESULT_OK) {
|
|
+ /*
|
|
+ * Log dropped or slipped responses in the query
|
|
+ * category so that requests are not silently lost.
|
|
+ * Starts of rate-limited bursts are logged in
|
|
+ * DNS_LOGCATEGORY_RRL.
|
|
+ *
|
|
+ * Dropped responses are counted with dropped queries
|
|
+ * in QryDropped while slipped responses are counted
|
|
+ * with other truncated responses in RespTruncated.
|
|
+ */
|
|
+ if (wouldlog) {
|
|
+ ns_client_log(client,
|
|
+ NS_LOGCATEGORY_QUERY_EERRORS,
|
|
+ NS_LOGMODULE_QUERY,
|
|
+ DNS_RRL_LOG_DROP,
|
|
+ "%s", log_buf);
|
|
+ }
|
|
+ if (!client->view->rrl->log_only) {
|
|
+ if (rrl_result == DNS_RRL_RESULT_DROP) {
|
|
+ /*
|
|
+ * These will also be counted in
|
|
+ * dns_nsstatscounter_dropped
|
|
+ */
|
|
+ inc_stats(client,
|
|
+ dns_nsstatscounter_ratedropped);
|
|
+ QUERY_ERROR(DNS_R_DROP);
|
|
+ } else {
|
|
+ /*
|
|
+ * These will also be counted in
|
|
+ * dns_nsstatscounter_truncatedresp
|
|
+ */
|
|
+ inc_stats(client,
|
|
+ dns_nsstatscounter_rateslipped);
|
|
+ client->message->flags |=
|
|
+ DNS_MESSAGEFLAG_TC;
|
|
+ if (resp_result == DNS_R_NXDOMAIN)
|
|
+ client->message->rcode =
|
|
+ dns_rcode_nxdomain;
|
|
+ }
|
|
+ goto cleanup;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ 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 ||
|
|
@@ -5910,7 +6389,8 @@
|
|
if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
|
|
rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU &&
|
|
rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
|
|
- if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME) {
|
|
+ if (rpz_st->m.type == DNS_RPZ_TYPE_QNAME ||
|
|
+ rpz_st->m.type == DNS_RPZ_TYPE_CLIENT_IP) {
|
|
result = dns_name_copy(client->query.qname,
|
|
fname, NULL);
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
@@ -5934,6 +6414,9 @@
|
|
rpz_st->m.zone = NULL;
|
|
|
|
switch (rpz_st->m.policy) {
|
|
+ case DNS_RPZ_POLICY_DROP:
|
|
+ QUERY_ERROR(DNS_R_DROP);
|
|
+ break;
|
|
case DNS_RPZ_POLICY_NXDOMAIN:
|
|
result = DNS_R_NXDOMAIN;
|
|
break;
|
|
@@ -6002,7 +6485,7 @@
|
|
rpz_st->q.is_zone = is_zone;
|
|
is_zone = ISC_TRUE;
|
|
rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy,
|
|
- rpz_st->m.type, zone, rpz_st->qname);
|
|
+ rpz_st->m.type, zone, rpz_st->p_name);
|
|
}
|
|
}
|
|
|
|
@@ -7318,12 +7801,14 @@
|
|
}
|
|
|
|
if (eresult != ISC_R_SUCCESS &&
|
|
- (!PARTIALANSWER(client) || WANTRECURSION(client))) {
|
|
+ (!PARTIALANSWER(client) || WANTRECURSION(client)
|
|
+ || eresult == DNS_R_DROP)) {
|
|
if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
|
|
/*
|
|
* This was a duplicate query that we are
|
|
- * recursing on. Don't send a response now.
|
|
- * The original query will still cause a response.
|
|
+ * recursing on or the result of rate limiting.
|
|
+ * Don't send a response now for a duplicate query,
|
|
+ * because the original will still cause a response.
|
|
*/
|
|
query_next(client, eresult);
|
|
} else {
|
|
diff -r -u bin/named/server.c-orig bin/named/server.c
|
|
--- bin/named/server.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/server.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -373,7 +373,8 @@
|
|
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);
|
|
@@ -1549,17 +1550,24 @@
|
|
}
|
|
|
|
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_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_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);
|
|
|
|
- new = isc_mem_get(view->mctx, sizeof(*new));
|
|
+ if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES)
|
|
+ return (ISC_R_NOMEMORY);
|
|
+
|
|
+ new = isc_mem_get(view->rpzs->mctx, sizeof(*new));
|
|
if (new == NULL) {
|
|
cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
|
|
"no memory for response policy zones");
|
|
@@ -1567,20 +1575,28 @@
|
|
}
|
|
|
|
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->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->cname, NULL);
|
|
- ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
|
|
+ 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)) {
|
|
@@ -1588,6 +1604,8 @@
|
|
} 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 = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
|
|
@@ -1598,25 +1616,45 @@
|
|
"invalid zone name '%s'", str);
|
|
return (DNS_R_EMPTYLABEL);
|
|
}
|
|
- 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)) {
|
|
+ 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 = 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_name2(view, rpz_obj, &new->nsdname,
|
|
DNS_RPZ_NSDNAME_ZONE, &new->origin);
|
|
if (result != ISC_R_SUCCESS)
|
|
return (result);
|
|
|
|
+ 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");
|
|
+ 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);
|
|
|
|
@@ -1635,10 +1673,282 @@
|
|
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);
|
|
}
|
|
|
|
+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) {
|
|
+ if (pview != NULL)
|
|
+ dns_view_detach(&pview);
|
|
+ return (result);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * 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);
|
|
+
|
|
+ return (ISC_R_SUCCESS);
|
|
+}
|
|
+
|
|
+#define CHECK_RRL(cond, pat, val1, val2) \
|
|
+ do { \
|
|
+ if (!(cond)) { \
|
|
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \
|
|
+ pat, val1, val2); \
|
|
+ result = ISC_R_RANGE; \
|
|
+ goto cleanup; \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+#define CHECK_RRL_RATE(rate, def, max_rate, name) \
|
|
+ do { \
|
|
+ obj = NULL; \
|
|
+ rrl->rate.str = name; \
|
|
+ result = cfg_map_get(map, name, &obj); \
|
|
+ if (result == ISC_R_SUCCESS) { \
|
|
+ rrl->rate.r = cfg_obj_asuint32(obj); \
|
|
+ CHECK_RRL(rrl->rate.r <= max_rate, \
|
|
+ name" %d > %d", \
|
|
+ rrl->rate.r, max_rate); \
|
|
+ } else { \
|
|
+ rrl->rate.r = def; \
|
|
+ } \
|
|
+ rrl->rate.scaled = rrl->rate.r; \
|
|
+ } while (0)
|
|
+
|
|
+static isc_result_t
|
|
+configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
|
|
+ const cfg_obj_t *obj;
|
|
+ dns_rrl_t *rrl;
|
|
+ isc_result_t result;
|
|
+ int min_entries, i, j;
|
|
+
|
|
+ /*
|
|
+ * Most DNS servers have few clients, but intentinally open
|
|
+ * recursive and authoritative servers often have many.
|
|
+ * So start with a small number of entries unless told otherwise
|
|
+ * to reduce cold-start costs.
|
|
+ */
|
|
+ min_entries = 500;
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "min-table-size", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ min_entries = cfg_obj_asuint32(obj);
|
|
+ if (min_entries < 1)
|
|
+ min_entries = 1;
|
|
+ }
|
|
+ result = dns_rrl_init(&rrl, view, min_entries);
|
|
+ if (result != ISC_R_SUCCESS)
|
|
+ return (result);
|
|
+
|
|
+ i = ISC_MAX(20000, min_entries);
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "max-table-size", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ i = cfg_obj_asuint32(obj);
|
|
+ CHECK_RRL(i >= min_entries,
|
|
+ "max-table-size %d < min-table-size %d",
|
|
+ i, min_entries);
|
|
+ }
|
|
+ rrl->max_entries = i;
|
|
+
|
|
+ CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
|
|
+ "responses-per-second");
|
|
+ CHECK_RRL_RATE(referrals_per_second,
|
|
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
|
+ "referrals-per-second");
|
|
+ CHECK_RRL_RATE(nodata_per_second,
|
|
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
|
+ "nodata-per-second");
|
|
+ CHECK_RRL_RATE(nxdomains_per_second,
|
|
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
|
+ "nxdomains-per-second");
|
|
+ CHECK_RRL_RATE(errors_per_second,
|
|
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
|
|
+ "errors-per-second");
|
|
+
|
|
+ CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
|
|
+ "all-per-second");
|
|
+
|
|
+ CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
|
|
+ "slip");
|
|
+
|
|
+ i = 15;
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "window", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ i = cfg_obj_asuint32(obj);
|
|
+ CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
|
|
+ "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
|
|
+ }
|
|
+ rrl->window = i;
|
|
+
|
|
+ i = 0;
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "qps-scale", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ i = cfg_obj_asuint32(obj);
|
|
+ CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
|
|
+ }
|
|
+ rrl->qps_scale = i;
|
|
+ rrl->qps = 1.0;
|
|
+
|
|
+ i = 24;
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "ipv4-prefix-length", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ i = cfg_obj_asuint32(obj);
|
|
+ CHECK_RRL(i >= 8 && i <= 32,
|
|
+ "invalid 'ipv4-prefix-length %d'%s", i, "");
|
|
+ }
|
|
+ rrl->ipv4_prefixlen = i;
|
|
+ if (i == 32)
|
|
+ rrl->ipv4_mask = 0xffffffff;
|
|
+ else
|
|
+ rrl->ipv4_mask = htonl(0xffffffff << (32-i));
|
|
+
|
|
+ i = 56;
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "ipv6-prefix-length", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ i = cfg_obj_asuint32(obj);
|
|
+ CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
|
|
+ "ipv6-prefix-length %d < 16 or > %d",
|
|
+ i, DNS_RRL_MAX_PREFIX);
|
|
+ }
|
|
+ rrl->ipv6_prefixlen = i;
|
|
+ for (j = 0; j < 4; ++j) {
|
|
+ if (i <= 0) {
|
|
+ rrl->ipv6_mask[j] = 0;
|
|
+ } else if (i < 32) {
|
|
+ rrl->ipv6_mask[j] = htonl(0xffffffff << (32-i));
|
|
+ } else {
|
|
+ rrl->ipv6_mask[j] = 0xffffffff;
|
|
+ }
|
|
+ i -= 32;
|
|
+ }
|
|
+
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "exempt-clients", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
|
|
+ ns_g_aclconfctx, ns_g_mctx,
|
|
+ 0, &rrl->exempt);
|
|
+ CHECK_RRL(result == ISC_R_SUCCESS,
|
|
+ "invalid %s%s", "address match list", "");
|
|
+ }
|
|
+
|
|
+ obj = NULL;
|
|
+ result = cfg_map_get(map, "log-only", &obj);
|
|
+ if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj))
|
|
+ rrl->log_only = ISC_TRUE;
|
|
+ else
|
|
+ rrl->log_only = ISC_FALSE;
|
|
+
|
|
+ return (ISC_R_SUCCESS);
|
|
+
|
|
+ cleanup:
|
|
+ dns_rrl_view_destroy(view);
|
|
+ return (result);
|
|
+}
|
|
+
|
|
/*
|
|
* Configure 'view' according to 'vconfig', taking defaults from 'config'
|
|
* where values are missing in 'vconfig'.
|
|
@@ -1705,7 +2015,7 @@
|
|
dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
|
|
unsigned int query_timeout, ndisp;
|
|
struct cfg_context *nzctx;
|
|
- dns_rpz_zone_t *rpz;
|
|
+ isc_boolean_t old_rpz_ok;
|
|
|
|
REQUIRE(DNS_VIEW_VALID(view));
|
|
|
|
@@ -1810,44 +2120,7 @@
|
|
obj = NULL;
|
|
if (view->rdclass == dns_rdataclass_in && need_hints &&
|
|
ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
|
|
- const cfg_obj_t *rpz_obj;
|
|
- isc_boolean_t recursive_only_def;
|
|
- dns_ttl_t ttl_def;
|
|
-
|
|
- rpz_obj = cfg_tuple_get(obj, "recursive-only");
|
|
- if (!cfg_obj_isvoid(rpz_obj) &&
|
|
- !cfg_obj_asboolean(rpz_obj))
|
|
- recursive_only_def = ISC_FALSE;
|
|
- else
|
|
- recursive_only_def = ISC_TRUE;
|
|
-
|
|
- rpz_obj = cfg_tuple_get(obj, "break-dnssec");
|
|
- if (!cfg_obj_isvoid(rpz_obj) &&
|
|
- cfg_obj_asboolean(rpz_obj))
|
|
- view->rpz_break_dnssec = ISC_TRUE;
|
|
- else
|
|
- view->rpz_break_dnssec = ISC_FALSE;
|
|
-
|
|
- rpz_obj = cfg_tuple_get(obj, "max-policy-ttl");
|
|
- if (cfg_obj_isuint32(rpz_obj))
|
|
- ttl_def = cfg_obj_asuint32(rpz_obj);
|
|
- else
|
|
- ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
|
|
-
|
|
- rpz_obj = cfg_tuple_get(obj, "min-ns-dots");
|
|
- if (cfg_obj_isuint32(rpz_obj))
|
|
- view->rpz_min_ns_labels = cfg_obj_asuint32(rpz_obj) + 1;
|
|
- else
|
|
- view->rpz_min_ns_labels = 2;
|
|
-
|
|
- element = cfg_list_first(cfg_tuple_get(obj, "zone list"));
|
|
- while (element != NULL) {
|
|
- result = configure_rpz(view, element,
|
|
- recursive_only_def, ttl_def);
|
|
- if (result != ISC_R_SUCCESS)
|
|
- goto cleanup;
|
|
- element = cfg_list_next(element);
|
|
- }
|
|
+ CHECK(configure_rpz(view, obj, &old_rpz_ok));
|
|
}
|
|
|
|
/*
|
|
@@ -1868,22 +2141,29 @@
|
|
{
|
|
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));
|
|
}
|
|
|
|
- for (rpz = ISC_LIST_HEAD(view->rpz_zones);
|
|
- rpz != NULL;
|
|
- rpz = ISC_LIST_NEXT(rpz, link))
|
|
- {
|
|
- if (!rpz->defined) {
|
|
- char namebuf[DNS_NAME_FORMATSIZE];
|
|
+ /*
|
|
+ * 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;
|
|
|
|
- dns_name_format(&rpz->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;
|
|
+ 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;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -1909,7 +2189,7 @@
|
|
const cfg_obj_t *zconfig = cfg_listelt_value(element);
|
|
CHECK(configure_zone(config, zconfig, vconfig,
|
|
mctx, view, actx,
|
|
- ISC_TRUE));
|
|
+ ISC_TRUE, ISC_FALSE));
|
|
}
|
|
}
|
|
|
|
@@ -3043,6 +3323,14 @@
|
|
}
|
|
}
|
|
|
|
+ obj = NULL;
|
|
+ result = ns_config_get(maps, "rate-limit", &obj);
|
|
+ if (result == ISC_R_SUCCESS) {
|
|
+ result = configure_rrl(view, config, obj);
|
|
+ if (result != ISC_R_SUCCESS)
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
result = ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
@@ -3375,7 +3663,8 @@
|
|
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 */
|
|
@@ -3396,8 +3685,7 @@
|
|
const char *zname;
|
|
dns_rdataclass_t zclass;
|
|
const char *ztypestr;
|
|
- isc_boolean_t is_rpz;
|
|
- dns_rpz_zone_t *rpz;
|
|
+ dns_rpz_num_t rpz_num;
|
|
|
|
options = NULL;
|
|
(void)cfg_map_get(config, "options", &options);
|
|
@@ -3559,18 +3847,15 @@
|
|
INSIST(dupzone == NULL);
|
|
|
|
/*
|
|
- * Note whether this is a response policy zone.
|
|
+ * Note whether this is a response policy zone and which one if so.
|
|
*/
|
|
- is_rpz = ISC_FALSE;
|
|
- for (rpz = ISC_LIST_HEAD(view->rpz_zones);
|
|
- rpz != NULL;
|
|
- rpz = ISC_LIST_NEXT(rpz, link))
|
|
- {
|
|
- if (dns_name_equal(&rpz->origin, origin)) {
|
|
- is_rpz = ISC_TRUE;
|
|
- rpz->defined = ISC_TRUE;
|
|
+ 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;
|
|
}
|
|
|
|
/*
|
|
@@ -3581,7 +3866,9 @@
|
|
* - 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 and is or was not and is not a policy 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);
|
|
@@ -3595,7 +3882,8 @@
|
|
if (zone != NULL && !ns_zone_reusable(zone, zconfig))
|
|
dns_zone_detach(&zone);
|
|
|
|
- if (zone != NULL && is_rpz != dns_zone_get_rpz(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) {
|
|
@@ -3620,8 +3908,8 @@
|
|
dns_zone_setstats(zone, ns_g_server->zonestats);
|
|
}
|
|
|
|
- if (is_rpz) {
|
|
- result = dns_zone_rpz_enable(zone);
|
|
+ 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,
|
|
@@ -7834,7 +8122,8 @@
|
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
|
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)
|
|
diff -r -u bin/named/statschannel.c-orig bin/named/statschannel.c
|
|
--- bin/named/statschannel.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/named/statschannel.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -206,6 +206,10 @@
|
|
SET_NSSTATDESC(updatebadprereq,
|
|
"updates rejected due to prerequisite failure",
|
|
"UpdateBadPrereq");
|
|
+ SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
|
|
+ "RateDropped");
|
|
+ SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
|
|
+ "RateSlipped");
|
|
SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
|
|
"RPZRewrites");
|
|
INSIST(i == dns_nsstatscounter_max);
|
|
diff -r -u bin/tests/system/README-orig bin/tests/system/README
|
|
--- bin/tests/system/README-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/README 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -17,6 +17,7 @@
|
|
nsupdate/ Dynamic update and IXFR tests
|
|
resolver/ Regression tests for resolver bugs that have been fixed
|
|
(not a complete resolver test suite)
|
|
+ rrl/ query rate limiting
|
|
rpz/ Tests of response policy zone (RPZ) rewriting
|
|
stub/ Tests of stub zone functionality
|
|
unknown/ Unknown type and class tests
|
|
diff -r -u bin/tests/system/conf.sh.in-orig bin/tests/system/conf.sh.in
|
|
--- bin/tests/system/conf.sh.in-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/conf.sh.in 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -62,7 +62,7 @@
|
|
database dlv dlvauto dlz dlzexternal dname dns64 dnssec ecdsa
|
|
formerr forward glue gost ixfr inline limits logfileconfig
|
|
lwresd masterfile masterformat metadata notify nsupdate pending
|
|
- pkcs11 redirect resolver rndc rpz rrsetorder rsabigexponent
|
|
+ pkcs11 redirect resolver rndc rpz rrl rrsetorder rsabigexponent
|
|
smartsign sortlist spf staticstub stub tkey tsig tsiggss unknown
|
|
upforwd verify views wildcard xfer xferquota zonechecks"
|
|
|
|
diff -r -u bin/tests/system/rpz/Makefile.in-orig bin/tests/system/rpz/Makefile.in
|
|
--- bin/tests/system/rpz/Makefile.in-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/Makefile.in 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
# PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-# $Id$
|
|
-
|
|
|
|
srcdir = @srcdir@
|
|
VPATH = @srcdir@
|
|
diff -r -u bin/tests/system/rpz/clean.sh-orig bin/tests/system/rpz/clean.sh
|
|
--- bin/tests/system/rpz/clean.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/clean.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
# PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-# $Id$
|
|
-
|
|
|
|
|
|
# Clean up after rpz tests.
|
|
diff -r -u bin/tests/system/rpz/ns1/named.conf-orig bin/tests/system/rpz/ns1/named.conf
|
|
--- bin/tests/system/rpz/ns1/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns1/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * Copyright (C) 2011, 2013 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
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
controls { /* empty */ };
|
|
|
|
diff -r -u bin/tests/system/rpz/ns1/root.db-orig bin/tests/system/rpz/ns1/root.db
|
|
--- bin/tests/system/rpz/ns1/root.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns1/root.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
$TTL 120
|
|
. SOA ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
|
|
diff -r -u bin/tests/system/rpz/ns2/base-tld2s.db-orig bin/tests/system/rpz/ns2/base-tld2s.db
|
|
--- bin/tests/system/rpz/ns2/base-tld2s.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns2/base-tld2s.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2012, 2013 Internet Systems Consortium, Inc. ("ISC")
|
|
+; 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
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
|
|
; RPZ rewrite responses from this signed zone
|
|
diff -r -u bin/tests/system/rpz/ns2/hints-orig bin/tests/system/rpz/ns2/hints
|
|
--- bin/tests/system/rpz/ns2/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns2/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2011, 2013 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
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
. 120 NS ns.
|
|
ns. 120 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rpz/ns2/named.conf-orig bin/tests/system/rpz/ns2/named.conf
|
|
--- bin/tests/system/rpz/ns2/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns2/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
|
|
controls { /* empty */ };
|
|
diff -r -u bin/tests/system/rpz/ns2/tld2.db-orig bin/tests/system/rpz/ns2/tld2.db
|
|
--- bin/tests/system/rpz/ns2/tld2.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns2/tld2.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
|
|
; RPZ rewrite responses from this zone
|
|
@@ -111,6 +109,9 @@
|
|
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"
|
|
|
|
diff -r -u bin/tests/system/rpz/ns3/base.db-orig bin/tests/system/rpz/ns3/base.db
|
|
--- bin/tests/system/rpz/ns3/base.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns3/base.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
|
|
; RPZ test
|
|
diff -r -u bin/tests/system/rpz/ns3/hints-orig bin/tests/system/rpz/ns3/hints
|
|
--- bin/tests/system/rpz/ns3/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns3/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2011, 2013 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
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
. 120 NS ns.
|
|
ns. 120 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rpz/ns3/named.conf-orig bin/tests/system/rpz/ns3/named.conf
|
|
--- bin/tests/system/rpz/ns3/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns3/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
|
|
/*
|
|
@@ -46,7 +44,10 @@
|
|
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;
|
|
+ zone "bl-drop" policy drop;
|
|
+ } min-ns-dots 0
|
|
+ # qname-wait-recurse no
|
|
+ ;
|
|
};
|
|
|
|
key rndc_key {
|
|
@@ -58,7 +59,6 @@
|
|
};
|
|
|
|
|
|
-// include "../trusted.conf";
|
|
zone "." { type hint; file "hints"; };
|
|
|
|
zone "bl." {type master; file "bl.db";
|
|
@@ -83,6 +83,8 @@
|
|
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 "crash1.tld2" {type master; file "crash1";};
|
|
zone "crash2.tld3." {type master; file "crash2";};
|
|
diff -r -u bin/tests/system/rpz/ns4/hints-orig bin/tests/system/rpz/ns4/hints
|
|
--- bin/tests/system/rpz/ns4/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns4/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2011, 2013 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
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
. 120 NS ns.
|
|
ns. 120 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rpz/ns4/named.conf-orig bin/tests/system/rpz/ns4/named.conf
|
|
--- bin/tests/system/rpz/ns4/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns4/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
controls { /* empty */ };
|
|
|
|
diff -r -u bin/tests/system/rpz/ns4/tld4.db-orig bin/tests/system/rpz/ns4/tld4.db
|
|
--- bin/tests/system/rpz/ns4/tld4.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns4/tld4.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
; RPZ rewrite responses from this zone
|
|
|
|
diff -r -u bin/tests/system/rpz/ns5/hints-orig bin/tests/system/rpz/ns5/hints
|
|
--- bin/tests/system/rpz/ns5/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns5/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
; PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-; $Id$
|
|
-
|
|
|
|
. 120 NS ns.
|
|
ns. 120 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rpz/ns5/named.conf-orig bin/tests/system/rpz/ns5/named.conf
|
|
--- bin/tests/system/rpz/ns5/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns5/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * Copyright (C) 2012, 2013 Internet Systems Consortium, Inc. ("ISC")
|
|
+ * 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
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
|
|
/*
|
|
@@ -56,3 +54,20 @@
|
|
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"; };
|
|
diff -r -u bin/tests/system/rpz/ns5/tld5.db-orig bin/tests/system/rpz/ns5/tld5.db
|
|
--- bin/tests/system/rpz/ns5/tld5.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/ns5/tld5.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
|
|
+; 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
|
|
diff -r -u bin/tests/system/rpz/qperf.sh-orig bin/tests/system/rpz/qperf.sh
|
|
--- bin/tests/system/rpz/qperf.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/qperf.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,6 +1,6 @@
|
|
#! /bin/sh
|
|
#
|
|
-# Copyright (C) 2012, 2013 Internet Systems Consortium, Inc. ("ISC")
|
|
+# 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
|
|
@@ -14,8 +14,6 @@
|
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
# PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-# $Id$
|
|
-
|
|
|
|
for QDIR in `echo "$PATH" | tr : ' '` ../../../../contrib/queryperf; do
|
|
QPERF=$QDIR/queryperf
|
|
diff -r -u bin/tests/system/rpz/rpz.c-orig bin/tests/system/rpz/rpz.c
|
|
--- bin/tests/system/rpz/rpz.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/rpz.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,8 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
-
|
|
|
|
#include <config.h>
|
|
|
|
diff -r -u bin/tests/system/rpz/setup.sh-orig bin/tests/system/rpz/setup.sh
|
|
--- bin/tests/system/rpz/setup.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/setup.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,8 +14,6 @@
|
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
# PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-# $Id$
|
|
-
|
|
|
|
set -e
|
|
|
|
@@ -30,7 +28,7 @@
|
|
# 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
|
|
+for NM in '' -2 -given -disabled -passthru -no-op -nodata -nxdomain -cname -wildcname -garden -drop; do
|
|
sed -e "/SOA/s/blx/bl$NM/g" ns3/base.db >ns3/bl$NM.db
|
|
done
|
|
|
|
@@ -51,7 +49,10 @@
|
|
# Performance checks.
|
|
cat <<EOF >ns5/rpz-switch
|
|
response-policy {
|
|
- zone "bl0"; zone "bl1"; zone "bl2";
|
|
+ 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
|
|
diff -r -u bin/tests/system/rpz/test1-orig bin/tests/system/rpz/test1
|
|
--- bin/tests/system/rpz/test1-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test1 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -24,13 +24,13 @@
|
|
; 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
|
|
-; 4, 5, 22, 23
|
|
+; 4, 5
|
|
update add a3-2.tld2.bl. 300 DNAME example.com.
|
|
;
|
|
; NXDOMAIN for a4-2-cname.tld2 via its target a4-2.tld2.
|
|
@@ -77,6 +77,14 @@
|
|
; 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
|
|
@@ -84,4 +92,6 @@
|
|
; 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.
|
|
send
|
|
diff -r -u bin/tests/system/rpz/test2-orig bin/tests/system/rpz/test2
|
|
--- bin/tests/system/rpz/test2-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test2 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -58,7 +58,7 @@
|
|
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.
|
|
;
|
|
@@ -72,3 +72,8 @@
|
|
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
|
|
diff -r -u bin/tests/system/rpz/test3-orig bin/tests/system/rpz/test3
|
|
--- bin/tests/system/rpz/test3-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test3 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2011, 2013 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
|
|
diff -r -u bin/tests/system/rpz/test4-orig bin/tests/system/rpz/test4
|
|
--- bin/tests/system/rpz/test4-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test4 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2011, 2013 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
|
|
diff -r -u bin/tests/system/rpz/test4a-orig bin/tests/system/rpz/test4a
|
|
--- bin/tests/system/rpz/test4a-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test4a 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1,4 +1,4 @@
|
|
-; Copyright (C) 2013 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
|
|
diff -r -u bin/tests/system/rpz/test5-orig bin/tests/system/rpz/test5
|
|
--- bin/tests/system/rpz/test5-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/test5 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -57,3 +57,6 @@
|
|
; 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
|
|
diff -r -u bin/tests/system/rpz/tests.sh-orig bin/tests/system/rpz/tests.sh
|
|
--- bin/tests/system/rpz/tests.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rpz/tests.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -12,8 +12,6 @@
|
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
# PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
-# $Id$
|
|
-
|
|
|
|
# test response policy zones (RPZ)
|
|
|
|
@@ -21,15 +19,19 @@
|
|
. $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
|
|
+
|
|
+# "response policy zone rewrites" from ns3/named.stats if none of the
|
|
+# conditional tests are done.
|
|
+NS3_STATS=50
|
|
|
|
USAGE="$0: [-x]"
|
|
while getopts "x" c; do
|
|
@@ -87,10 +89,10 @@
|
|
# (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
|
|
echo "I:failed to update policy zone with $TEST_FILE"
|
|
exit 1
|
|
- }
|
|
+ fi
|
|
fi
|
|
}
|
|
|
|
@@ -213,6 +215,13 @@
|
|
ckresult "$*" proto.nodata
|
|
}
|
|
|
|
+# check dropped response
|
|
+drop () {
|
|
+ make_dignm
|
|
+ digcmd $* >$DIGNM
|
|
+ ckresult "$*" proto.drop
|
|
+}
|
|
+
|
|
# check rewrite to an address
|
|
# modify the output so that it is easily compared, but save the original line
|
|
# $1=IPv4 address $2=digcmd args $3=optional TTL
|
|
@@ -239,7 +248,7 @@
|
|
nochange () {
|
|
make_dignm
|
|
digcmd $* >$DIGNM
|
|
- digcmd $* @$ns2 >${DIGNM}_OK
|
|
+ digcmd $* @$ns5 >${DIGNM}_OK
|
|
ckresult "$*" ${DIGNM}_OK && clean_result ${DIGNM}_OK
|
|
}
|
|
|
|
@@ -254,6 +263,12 @@
|
|
# make prototype files to check against rewritten results
|
|
digcmd nonexistent @$ns2 >proto.nxdomain
|
|
digcmd txt-only.tld2 @$ns2 >proto.nodata
|
|
+cat <<EOF >proto.drop
|
|
+
|
|
+; hello
|
|
+;; global options:
|
|
+;; connection timed out; no servers could be reached
|
|
+EOF
|
|
|
|
|
|
status=0
|
|
@@ -278,18 +293,19 @@
|
|
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
|
|
+drop a3-8.tld2 # 32 drop
|
|
end_group
|
|
|
|
start_group "IP rewrites" test2
|
|
@@ -305,10 +321,11 @@
|
|
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
|
|
+addr 127.0.0.17 "a4-4.tld2 -b $ns1" # 17 client-IP address trigger
|
|
end_group
|
|
|
|
# check that IP addresses for previous group were deleted from the radix tree
|
|
@@ -356,15 +373,15 @@
|
|
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 + 1`
|
|
+ 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 configured with --disable-rpz-nsip"
|
|
fi
|
|
@@ -393,6 +410,7 @@
|
|
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
|
|
end_group
|
|
|
|
# check that miscellaneous bugs are still absent
|
|
@@ -412,6 +430,9 @@
|
|
end_group
|
|
|
|
|
|
+ckstats $ns3 ns3 $NS3_STATS
|
|
+
|
|
+
|
|
# superficial test for major performance bugs
|
|
QPERF=`sh qperf.sh`
|
|
if test -n "$QPERF"; then
|
|
@@ -464,8 +485,6 @@
|
|
fi
|
|
|
|
|
|
-ckstats $ns3 ns3 55
|
|
-
|
|
# 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
|
|
diff -r -u bin/tests/system/rrl/clean.sh-orig bin/tests/system/rrl/clean.sh
|
|
--- bin/tests/system/rrl/clean.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/clean.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,21 @@
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+# Clean up after rrl tests.
|
|
+
|
|
+rm -f dig.out*
|
|
+rm -f */named.memstats */named.run */named.stats */log-* */session.key
|
|
+rm -f ns3/bl*.db */*.jnl */*.core */*.pid
|
|
diff -r -u bin/tests/system/rrl/ns1/named.conf-orig bin/tests/system/rrl/ns1/named.conf
|
|
--- bin/tests/system/rrl/ns1/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns1/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,32 @@
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.1;
|
|
+ notify-source 10.53.0.1;
|
|
+ transfer-source 10.53.0.1;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ listen-on { 10.53.0.1; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+};
|
|
+
|
|
+zone "." {type master; file "root.db";};
|
|
diff -r -u bin/tests/system/rrl/ns1/root.db-orig bin/tests/system/rrl/ns1/root.db
|
|
--- bin/tests/system/rrl/ns1/root.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns1/root.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,31 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+$TTL 120
|
|
+@ SOA ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
|
|
+@ NS ns.
|
|
+ns. A 10.53.0.1
|
|
+. A 10.53.0.1
|
|
+
|
|
+; limit responses from here
|
|
+tld2. NS ns.tld2.
|
|
+ns.tld2. A 10.53.0.2
|
|
+
|
|
+; limit recursion to here
|
|
+tld3. NS ns.tld3.
|
|
+ns.tld3. A 10.53.0.3
|
|
+
|
|
+; generate SERVFAIL
|
|
+tld4. NS ns.tld3.
|
|
diff -r -u bin/tests/system/rrl/ns2/hints-orig bin/tests/system/rrl/ns2/hints
|
|
--- bin/tests/system/rrl/ns2/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns2/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,18 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+. 0 NS ns1.
|
|
+ns1. 0 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rrl/ns2/named.conf-orig bin/tests/system/rrl/ns2/named.conf
|
|
--- bin/tests/system/rrl/ns2/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns2/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,71 @@
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.2;
|
|
+ notify-source 10.53.0.2;
|
|
+ transfer-source 10.53.0.2;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ statistics-file "named.stats";
|
|
+ listen-on { 10.53.0.2; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+
|
|
+ rate-limit {
|
|
+ responses-per-second 2;
|
|
+ all-per-second 50;
|
|
+ slip 3;
|
|
+ exempt-clients { 10.53.0.7; };
|
|
+
|
|
+ // small enough to force a table expansion
|
|
+ min-table-size 75;
|
|
+ };
|
|
+
|
|
+ additional-from-cache no;
|
|
+};
|
|
+
|
|
+key rndc_key {
|
|
+ secret "1234abcd8765";
|
|
+ algorithm hmac-sha256;
|
|
+};
|
|
+controls {
|
|
+ inet 10.53.0.2 port 9953 allow { any; } keys { rndc_key; };
|
|
+};
|
|
+
|
|
+/*
|
|
+ * These log settings have no effect unless "-g" is removed from ../../start.pl
|
|
+ */
|
|
+logging {
|
|
+ channel debug {
|
|
+ file "log-debug";
|
|
+ print-category yes; print-severity yes; severity debug 10;
|
|
+ };
|
|
+ channel queries {
|
|
+ file "log-queries";
|
|
+ print-category yes; print-severity yes; severity info;
|
|
+ };
|
|
+ category rate-limit { debug; queries; };
|
|
+ category queries { debug; queries; };
|
|
+};
|
|
+
|
|
+zone "." { type hint; file "hints"; };
|
|
+
|
|
+zone "tld2."{ type master; file "tld2.db"; };
|
|
diff -r -u bin/tests/system/rrl/ns2/tld2.db-orig bin/tests/system/rrl/ns2/tld2.db
|
|
--- bin/tests/system/rrl/ns2/tld2.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns2/tld2.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,47 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+; rate limit response from this zone
|
|
+
|
|
+$TTL 120
|
|
+@ SOA tld2. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 )
|
|
+ NS ns
|
|
+ NS .
|
|
+ns A 10.53.0.2
|
|
+
|
|
+; basic rate limiting
|
|
+a1 A 192.0.2.1
|
|
+
|
|
+; wildcards
|
|
+*.a2 A 192.0.2.2
|
|
+
|
|
+; a3 is in tld3
|
|
+
|
|
+; a4 does not exist to give NXDOMAIN
|
|
+
|
|
+; a5 for TCP requests
|
|
+a5 A 192.0.2.5
|
|
+
|
|
+; a6 for whitelisted clients
|
|
+a6 A 192.0.2.6
|
|
+
|
|
+; a7 for SERVFAIL
|
|
+
|
|
+; a8 for NODATA
|
|
+a8 A 192.0.2.8
|
|
+
|
|
+; a9 for all-per-second limit
|
|
+$GENERATE 101-180 all$.a9 A 192.0.2.8
|
|
diff -r -u bin/tests/system/rrl/ns3/hints-orig bin/tests/system/rrl/ns3/hints
|
|
--- bin/tests/system/rrl/ns3/hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns3/hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,18 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+. 0 NS ns1.
|
|
+ns1. 0 A 10.53.0.1
|
|
diff -r -u bin/tests/system/rrl/ns3/named.conf-orig bin/tests/system/rrl/ns3/named.conf
|
|
--- bin/tests/system/rrl/ns3/named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns3/named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,50 @@
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.3;
|
|
+ notify-source 10.53.0.3;
|
|
+ transfer-source 10.53.0.3;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ listen-on { 10.53.0.3; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+
|
|
+ // check that all of the options are parsed without limiting anything
|
|
+ rate-limit {
|
|
+ responses-per-second 200;
|
|
+ referrals-per-second 220;
|
|
+ nodata-per-second 230;
|
|
+ nxdomains-per-second 240;
|
|
+ errors-per-second 250;
|
|
+ all-per-second 700;
|
|
+ ipv4-prefix-length 24;
|
|
+ ipv6-prefix-length 64;
|
|
+ qps-scale 10;
|
|
+ window 1;
|
|
+ max-table-size 1000;
|
|
+ };
|
|
+
|
|
+};
|
|
+
|
|
+zone "." { type hint; file "hints"; };
|
|
+
|
|
+zone "tld3."{ type master; file "tld3.db"; };
|
|
diff -r -u bin/tests/system/rrl/ns3/tld3.db-orig bin/tests/system/rrl/ns3/tld3.db
|
|
--- bin/tests/system/rrl/ns3/tld3.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/ns3/tld3.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,25 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+; rate limit response from this zone
|
|
+
|
|
+$TTL 120
|
|
+@ SOA tld3. hostmaster.ns.tld3. ( 1 3600 1200 604800 60 )
|
|
+ NS ns
|
|
+ NS .
|
|
+ns A 10.53.0.3
|
|
+
|
|
+*.a3 A 192.0.3.3
|
|
diff -r -u bin/tests/system/rrl/setup.sh-orig bin/tests/system/rrl/setup.sh
|
|
--- bin/tests/system/rrl/setup.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/setup.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,21 @@
|
|
+#!/bin/sh
|
|
+#
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+SYSTEMTESTTOP=..
|
|
+. $SYSTEMTESTTOP/conf.sh
|
|
+. ./clean.sh
|
|
+
|
|
diff -r -u bin/tests/system/rrl/tests.sh-orig bin/tests/system/rrl/tests.sh
|
|
--- bin/tests/system/rrl/tests.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ bin/tests/system/rrl/tests.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,258 @@
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+# test response rate limiting
|
|
+
|
|
+SYSTEMTESTTOP=..
|
|
+. $SYSTEMTESTTOP/conf.sh
|
|
+
|
|
+#set -x
|
|
+
|
|
+ns1=10.53.0.1 # root, defining the others
|
|
+ns2=10.53.0.2 # test server
|
|
+ns3=10.53.0.3 # secondary test server
|
|
+ns7=10.53.0.7 # whitelisted client
|
|
+
|
|
+USAGE="$0: [-x]"
|
|
+while getopts "x" c; do
|
|
+ case $c in
|
|
+ x) set -x;;
|
|
+ *) echo "$USAGE" 1>&2; exit 1;;
|
|
+ esac
|
|
+done
|
|
+shift `expr $OPTIND - 1 || true`
|
|
+if test "$#" -ne 0; then
|
|
+ echo "$USAGE" 1>&2
|
|
+ exit 1
|
|
+fi
|
|
+# really quit on control-C
|
|
+trap 'exit 1' 1 2 15
|
|
+
|
|
+
|
|
+ret=0
|
|
+setret () {
|
|
+ ret=1
|
|
+ echo "$*"
|
|
+}
|
|
+
|
|
+
|
|
+# Wait until soon after the start of a second to make results consistent.
|
|
+# The start of a second credits a rate limit.
|
|
+# This would be far easier in C or by assuming a modern version of perl.
|
|
+sec_start () {
|
|
+ START=`date`
|
|
+ while true; do
|
|
+ NOW=`date`
|
|
+ if test "$START" != "$NOW"; then
|
|
+ return
|
|
+ fi
|
|
+ $PERL -e 'select(undef, undef, undef, 0.05)' || true
|
|
+ done
|
|
+}
|
|
+
|
|
+
|
|
+# turn off ${HOME}/.digrc
|
|
+HOME=/dev/null; export HOME
|
|
+
|
|
+# $1=result name $2=domain name $3=dig options
|
|
+digcmd () {
|
|
+ OFILE=$1; shift
|
|
+ DIG_DOM=$1; shift
|
|
+ ARGS="+nosearch +time=1 +tries=1 +ignore -p 5300 $* $DIG_DOM @$ns2"
|
|
+ #echo I:dig $ARGS 1>&2
|
|
+ START=`date +%y%m%d%H%M.%S`
|
|
+ RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \
|
|
+ | sed -n -e '/^;; AUTHORITY/,/^$/d' \
|
|
+ -e '/^;; ADDITIONAL/,/^$/d' \
|
|
+ -e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \
|
|
+ -e 's/;; flags.* tc .*/TC/p' \
|
|
+ -e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \
|
|
+ -e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \
|
|
+ -e 's/;; connection timed out.*/drop/p' \
|
|
+ -e 's/;; communications error to.*/drop/p' \
|
|
+ | tr -d '\n'`
|
|
+ mv "$OFILE=TEMP" "$OFILE=$RESULT"
|
|
+ touch -t $START "$OFILE=$RESULT"
|
|
+}
|
|
+
|
|
+
|
|
+# $1=number of tests $2=target domain $3=dig options
|
|
+QNUM=1
|
|
+burst () {
|
|
+ BURST_LIMIT=$1; shift
|
|
+ BURST_DOM_BASE="$1"; shift
|
|
+ while test "$BURST_LIMIT" -ge 1; do
|
|
+ CNT=`expr "00$QNUM" : '.*\(...\)'`
|
|
+ eval BURST_DOM="$BURST_DOM_BASE"
|
|
+ FILE="dig.out-$BURST_DOM-$CNT"
|
|
+ digcmd $FILE $BURST_DOM $* &
|
|
+ QNUM=`expr $QNUM + 1`
|
|
+ BURST_LIMIT=`expr "$BURST_LIMIT" - 1`
|
|
+ done
|
|
+}
|
|
+
|
|
+
|
|
+# $1=domain $2=IP address $3=# of IP addresses $4=TC $5=drop
|
|
+# $6=NXDOMAIN $7=SERVFAIL or other errors
|
|
+ck_result() {
|
|
+ BAD=
|
|
+ wait
|
|
+ ADDRS=`ls dig.out-$1-*=$2 2>/dev/null | wc -l`
|
|
+ # count simple truncated and truncated NXDOMAIN as TC
|
|
+ TC=`ls dig.out-$1-*=TC dig.out-$1-*=NXDOMAINTC 2>/dev/null | wc -l`
|
|
+ DROP=`ls dig.out-$1-*=drop 2>/dev/null | wc -l`
|
|
+ # count NXDOMAIN and truncated NXDOMAIN as NXDOMAIN
|
|
+ NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN dig.out-$1-*=NXDOMAINTC 2>/dev/null \
|
|
+ | wc -l`
|
|
+ SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l`
|
|
+ if test $ADDRS -ne "$3"; then
|
|
+ setret "I:"$ADDRS" instead of $3 '$2' responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $TC -ne "$4"; then
|
|
+ setret "I:"$TC" instead of $4 truncation responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $DROP -ne "$5"; then
|
|
+ setret "I:"$DROP" instead of $5 dropped responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $NXDOMAIN -ne "$6"; then
|
|
+ setret "I:"$NXDOMAIN" instead of $6 NXDOMAIN responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $SERVFAIL -ne "$7"; then
|
|
+ setret "I:"$SERVFAIL" instead of $7 error responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test -z "$BAD"; then
|
|
+ rm -f dig.out-$1-*
|
|
+ fi
|
|
+}
|
|
+
|
|
+
|
|
+ckstats () {
|
|
+ LABEL="$1"; shift
|
|
+ TYPE="$1"; shift
|
|
+ EXPECTED="$1"; shift
|
|
+ C=`sed -n -e "s/[ ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p" \
|
|
+ ns2/named.stats | tail -1`
|
|
+ C=`expr 0$C + 0`
|
|
+ if test "$C" -ne $EXPECTED; then
|
|
+ setret "I:wrong $LABEL $TYPE statistics of $C instead of $EXPECTED"
|
|
+ fi
|
|
+}
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+# Tests of referrals to "." must be done before the hints are loaded
|
|
+# or with "additional-from-cache no"
|
|
+burst 5 a1.tld3 +norec
|
|
+# basic rate limiting
|
|
+burst 3 a1.tld2
|
|
+# 1 second delay allows an additional response.
|
|
+sleep 1
|
|
+burst 10 a1.tld2
|
|
+# Request 30 different qnames to try a wildcard.
|
|
+burst 30 'x$CNT.a2.tld2'
|
|
+# These should be counted and limited but are not. See RT33138.
|
|
+burst 10 'y.x$CNT.a2.tld2'
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# referrals to "."
|
|
+ck_result a1.tld3 '' 2 1 2 0 0
|
|
+# check 13 results including 1 second delay that allows an additional response
|
|
+ck_result a1.tld2 192.0.2.1 3 4 6 0 0
|
|
+
|
|
+# Check the wild card answers.
|
|
+# The parent name of the 30 requests is counted.
|
|
+ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0
|
|
+
|
|
+# These should be limited but are not. See RT33138.
|
|
+ck_result 'y.x*.a2.tld2' 192.0.2.2 10 0 0 0 0
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+burst 10 'x.a3.tld3'
|
|
+burst 10 'y$CNT.a3.tld3'
|
|
+burst 10 'z$CNT.a4.tld2'
|
|
+
|
|
+# 10 identical recursive responses are limited
|
|
+ck_result 'x.a3.tld3' 192.0.3.3 2 3 5 0 0
|
|
+
|
|
+# 10 different recursive responses are not limited
|
|
+ck_result 'y*.a3.tld3' 192.0.3.3 10 0 0 0 0
|
|
+
|
|
+# 10 different NXDOMAIN responses are limited based on the parent name.
|
|
+# We count 13 responses because we count truncated NXDOMAIN responses
|
|
+# as both truncated and NXDOMAIN.
|
|
+ck_result 'z*.a4.tld2' x 0 3 5 5 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats first dropped 36
|
|
+ckstats first truncated 21
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+burst 10 a5.tld2 +tcp
|
|
+burst 10 a6.tld2 -b $ns7
|
|
+burst 10 a7.tld4
|
|
+burst 2 a8.tld2 AAAA
|
|
+burst 2 a8.tld2 TXT
|
|
+burst 2 a8.tld2 SPF
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# TCP responses are not rate limited
|
|
+ck_result a5.tld2 192.0.2.5 10 0 0 0 0
|
|
+
|
|
+# whitelisted client is not rate limited
|
|
+ck_result a6.tld2 192.0.2.6 10 0 0 0 0
|
|
+
|
|
+# Errors such as SERVFAIL are rate limited.
|
|
+ck_result a7.tld4 x 0 0 8 0 2
|
|
+
|
|
+# NODATA responses are counted as the same regardless of qtype.
|
|
+ck_result a8.tld2 '' 2 2 2 0 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats second dropped 46
|
|
+ckstats second truncated 23
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# all-per-second
|
|
+# The qnames are all unique but the client IP address is constant.
|
|
+QNUM=101
|
|
+burst 60 'all$CNT.a9.tld2'
|
|
+
|
|
+ck_result 'a*.a9.tld2' 192.0.2.8 50 0 10 0 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats final dropped 56
|
|
+ckstats final truncated 23
|
|
+
|
|
+
|
|
+echo "I:exit status: $ret"
|
|
+# exit $ret
|
|
+[ $ret -ne 0 ] && echo "I:test failure overridden"
|
|
+exit 0
|
|
diff -r -u clean.sh-orig clean.sh
|
|
--- clean.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ clean.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,21 @@
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+# Clean up after rrl tests.
|
|
+
|
|
+rm -f dig.out*
|
|
+rm -f */named.memstats */named.run */named.stats */log-* */session.key
|
|
+rm -f ns3/bl*.db */*.jnl */*.core */*.pid
|
|
diff -r -u doc/arm/Bv9ARM-book.xml-orig doc/arm/Bv9ARM-book.xml
|
|
--- doc/arm/Bv9ARM-book.xml-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ doc/arm/Bv9ARM-book.xml 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -4818,6 +4818,32 @@
|
|
</para>
|
|
</entry>
|
|
</row>
|
|
+ <row rowsep="0">
|
|
+ <entry colname="1">
|
|
+ <para><command>rate-limit</command></para>
|
|
+ </entry>
|
|
+ <entry colname="2">
|
|
+ <para>
|
|
+ The start, periodic, and final notices of the
|
|
+ rate limiting of a stream of responses are logged at
|
|
+ <command>info</command> severity in this category.
|
|
+ These messages include a hash value of the domain name
|
|
+ of the response and the name itself,
|
|
+ except when there is insufficient memory to record
|
|
+ the name for the final notice
|
|
+ The final notice is normally delayed until about one
|
|
+ minute after rate limit stops.
|
|
+ A lack of memory can hurry the final notice,
|
|
+ in which case it starts with an asterisk (*).
|
|
+ Various internal events are logged at debug 1 level
|
|
+ and higher.
|
|
+ </para>
|
|
+ <para>
|
|
+ Rate limiting of individual requests
|
|
+ is logged in the <command>query-errors</command> category.
|
|
+ </para>
|
|
+ </entry>
|
|
+ </row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
@@ -5318,7 +5344,7 @@
|
|
<optional> match-mapped-addresses <replaceable>yes_or_no</replaceable>; </optional>
|
|
<optional> filter-aaaa-on-v4 ( <replaceable>yes_or_no</replaceable> | <replaceable>break-dnssec</replaceable> ); </optional>
|
|
<optional> filter-aaaa { <replaceable>address_match_list</replaceable> }; </optional>
|
|
- <optional> dns64 <replaceable>IPv6-prefix</replaceable> {
|
|
+ <optional> dns64 <replaceable>ipv6-prefix</replaceable> {
|
|
<optional> clients { <replaceable>address_match_list</replaceable> }; </optional>
|
|
<optional> mapped { <replaceable>address_match_list</replaceable> }; </optional>
|
|
<optional> exclude { <replaceable>address_match_list</replaceable> }; </optional>
|
|
@@ -5351,8 +5377,25 @@
|
|
<optional> resolver-query-timeout <replaceable>number</replaceable> ; </optional>
|
|
<optional> deny-answer-addresses { <replaceable>address_match_list</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
|
|
<optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
|
|
+ <optional> rate-limit {
|
|
+ <optional> responses-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> referrals-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> nodata-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> nxdomains-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> errors-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> all-per-second <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> window <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> log-only <replaceable>yes_or_no</replaceable> ; </optional>
|
|
+ <optional> qps-scale <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> ipv4-prefix-length <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> ipv6-prefix-length <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> slip <replaceable>number</replaceable> ; </optional>
|
|
+ <optional> exempt-clients { <replaceable>address_match_list</replaceable> } ; </optional>
|
|
+ <optional> max-table-size <replaceable>number</replaceable> ; </optional>
|
|
+ <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> ; </optional>
|
|
@@ -9645,30 +9688,25 @@
|
|
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>.
|
|
</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>
|
|
-
|
|
- <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
|
|
+ Five policy triggers can be encoded in RPZ records, CLIENT-IP,
|
|
+ QNAME, IP, NSDNAME, and NSIP.
|
|
+ CLIENT-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
|
|
+ <userinput>rpz-client-ip</userinput> 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 prefix length must be between 1 and 32.
|
|
+ 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.
|
|
@@ -9678,10 +9716,25 @@
|
|
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>
|
|
+ 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 prefix length must be between 1 and 128.
|
|
+ The IPv6 prefix length must be between 64 and 128.
|
|
+ </para>
|
|
+
|
|
+ <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>
|
|
+
|
|
+ <para>
|
|
+ The third kind of trigger is an IP address in an A and AAAA
|
|
+ record in the ANSWER section of a response.
|
|
+ IP address triggers
|
|
+ IP triggers are encoded like client-IP triggers except as
|
|
+ subdomains of <userinput>rpz-ip</userinput>.
|
|
</para>
|
|
|
|
<para>
|
|
@@ -9703,7 +9756,7 @@
|
|
</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
|
|
@@ -9714,8 +9767,8 @@
|
|
<listitem>Choose the triggered record in the zone that appears
|
|
first in the response-policy 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.
|
|
@@ -9734,14 +9787,25 @@
|
|
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.
|
|
+ all response policy zones are again consulted for the
|
|
+ DNAME or CNAME names and addresses.
|
|
</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>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.
|
|
+ </listitem>
|
|
+ <listitem>The <command>DROP</command> policy is specified
|
|
+ by a CNAME whose target is <command>rpz-drop</command>.
|
|
+ It causes the response to discarded.
|
|
+ Nothing is sent to the DNS client.
|
|
+ </listitem>
|
|
<listitem>The <command>NXDOMAIN</command> response is encoded
|
|
by a CNAME whose target is the root domain (.)
|
|
</listitem>
|
|
@@ -9761,23 +9825,16 @@
|
|
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>
|
|
</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."
|
|
@@ -9808,9 +9865,10 @@
|
|
</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
|
|
@@ -9819,15 +9877,37 @@
|
|
</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>
|
|
@@ -9897,6 +9977,223 @@
|
|
<command>RPZRewrites</command> statistics.
|
|
</para>
|
|
</sect3>
|
|
+
|
|
+ <sect3>
|
|
+ <title>Response Rate Limiting</title>
|
|
+ <para>
|
|
+ Excessive almost-identical UDP <emphasis>responses</emphasis>
|
|
+ can be controlled by configuring a
|
|
+ <command>rate-limit</command> clause in an
|
|
+ <command>options</command> or <command>view</command> statement.
|
|
+ This mechanism keeps authoritative BIND 9 from being used
|
|
+ in amplifying reflection denial of service (DoS) attacks.
|
|
+ Short truncated (TC=1) responses can be sent to provide
|
|
+ rate-limited responses to legitimate clients within
|
|
+ a range of forged, attacked IP addresses.
|
|
+ Legitimate clients react to dropped or truncated response
|
|
+ by retrying with UDP or with TCP respectively.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ This mechanism is intended for authoritative DNS servers.
|
|
+ It can be used on recursive servers but can slow
|
|
+ applications such as SMTP servers (mail receivers) and
|
|
+ HTTP clients (web browsers) that repeatedly request the
|
|
+ same domains.
|
|
+ When possible, closing "open" recursive servers is better.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Response rate limiting uses a "credit" or "token bucket" scheme.
|
|
+ Each combination of identical response and client
|
|
+ has a conceptual account that earns a specified number
|
|
+ of credits every second.
|
|
+ A prospective response debits its account by one.
|
|
+ Responses are dropped or truncated
|
|
+ while the account is negative.
|
|
+ Responses are tracked within a rolling window of time
|
|
+ which defaults to 15 seconds, but can be configured with
|
|
+ the <command>window</command> option to any value from
|
|
+ 1 to 3600 seconds (1 hour).
|
|
+ The account cannot become more positive than
|
|
+ the per-second limit
|
|
+ or more negative than <command>window</command>
|
|
+ times the per-second limit.
|
|
+ When the specified number of credits for a class of
|
|
+ responses is set to 0, those responses are not rate limited.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ The notions of "identical response" and "DNS client"
|
|
+ for rate limiting are not simplistic.
|
|
+ All responses to an address block are counted as if to a
|
|
+ single client.
|
|
+ The prefix lengths of addresses blocks are
|
|
+ specified with <command>ipv4-prefix-length</command> (default 24)
|
|
+ and <command>ipv6-prefix-length</command> (default 56).
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ All non-empty responses for a valid domain name (qname)
|
|
+ and record type (qtype) are identical and have a limit specified
|
|
+ with <command>responses-per-second</command>
|
|
+ (default 0 or no limit).
|
|
+ All empty (NODATA) responses for a valid domain,
|
|
+ regardless of query type, are identical.
|
|
+ Responses in the NODATA class are limited by
|
|
+ <command>nodata-per-second</command>
|
|
+ (default <command>responses-per-second</command>).
|
|
+ Requests for any and all undefined subdomains of a given
|
|
+ valid domain result in NXDOMAIN errors, and are identical
|
|
+ regardless of query type.
|
|
+ They are limited by <command>nxdomain-per-second</command>
|
|
+ (default <command>responses-per-second</command>).
|
|
+ This controls some attacks using random names, but
|
|
+ can be relaxed or turned off (set to 0)
|
|
+ on servers that expect many legitimate
|
|
+ NXDOMAIN responses, such as from anti-spam blacklists.
|
|
+ Referrals or delegations to the server of a given
|
|
+ domain are identical and are limited by
|
|
+ <command>referrals-per-second</command>
|
|
+ (default <command>responses-per-second</command>).
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Responses generated from local wildcards are counted and limited
|
|
+ as if they were for the parent domain name.
|
|
+ This controls flooding using random.wild.example.com.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ All requests that result in DNS errors other
|
|
+ than NXDOMAIN, such as SERVFAIL and FORMERR, are identical
|
|
+ regardless of requested name (qname) or record type (qtype).
|
|
+ This controls attacks using invalid requests or distant,
|
|
+ broken authoritative servers.
|
|
+ By default the limit on errors is the same as the
|
|
+ <command>responses-per-second</command> value,
|
|
+ but it can be set separately with
|
|
+ <command>errors-per-second</command>.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Many attacks using DNS involve UDP requests with forged source
|
|
+ addresses.
|
|
+ Rate limiting prevents the use of BIND 9 to flood a network
|
|
+ with responses to requests with forged source addresses,
|
|
+ but could let a third party block responses to legitimate requests.
|
|
+ There is a mechanism that can answer some legitimate
|
|
+ requests from a client whose address is being forged in a flood.
|
|
+ Setting <command>slip</command> to 2 (its default) causes every
|
|
+ other UDP request to be answered with a small truncated (TC=1)
|
|
+ response.
|
|
+ The small size and reduced frequency, and so lack of
|
|
+ amplification, of "slipped" responses make them unattractive
|
|
+ for reflection DoS attacks.
|
|
+ <command>slip</command> must be between 0 and 10.
|
|
+ A value of 0 does not "slip";
|
|
+ no truncated responses are sent due to rate limiting.
|
|
+ Some error responses including REFUSED and SERVFAIL
|
|
+ cannot be replaced with truncated responses and are instead
|
|
+ leaked at the <command>slip</command> rate.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ When the approximate query per second rate exceeds
|
|
+ the <command>qps-scale</command> value,
|
|
+ then the <command>responses-per-second</command>,
|
|
+ <command>errors-per-second</command>,
|
|
+ <command>nxdomains-per-second</command> and
|
|
+ <command>all-per-second</command> values are reduced by the
|
|
+ ratio of the current rate to the <command>qps-scale</command> value.
|
|
+ This feature can tighten defenses during attacks.
|
|
+ For example, with
|
|
+ <command>qps-scale 250; responses-per-second 20;</command> and
|
|
+ a total query rate of 1000 queries/second for all queries from
|
|
+ all DNS clients including via TCP,
|
|
+ then the effective responses/second limit changes to
|
|
+ (250/1000)*20 or 5.
|
|
+ Responses sent via TCP are not limited
|
|
+ but are counted to compute the query per second rate.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Communities of DNS clients can be given their own parameters or no
|
|
+ rate limiting by putting
|
|
+ <command>rate-limit</command> statements in <command>view</command>
|
|
+ statements instead of the global <command>option</command>
|
|
+ statement.
|
|
+ A <command>rate-limit</command> statement in a view replaces,
|
|
+ rather than supplementing, a <command>rate-limit</command>
|
|
+ statement among the main options.
|
|
+ DNS clients within a view can be exempted from rate limits
|
|
+ with the <command>exempt-clients</command> clause.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ UDP responses of all kinds can be limited with the
|
|
+ <command>all-per-second</command> phrase.
|
|
+ This rate limiting is unlike the rate limiting provided by
|
|
+ <command>responses-per-second</command>,
|
|
+ <command>errors-per-second</command>, and
|
|
+ <command>nxdomains-per-second</command> on a DNS server
|
|
+ which are often invisible to the victim of a DNS reflection attack.
|
|
+ Unless the forged requests of the attack are the same as the
|
|
+ legitimate requests of the victim, the victim's requests are
|
|
+ not affected.
|
|
+ Responses affected by an <command>all-per-second</command> limit
|
|
+ are always dropped; the <command>slip</command> value has no
|
|
+ effect.
|
|
+ An <command>all-per-second</command> limit should be
|
|
+ at least 4 times as large as the other limits,
|
|
+ because single DNS clients often send bursts of legitimate
|
|
+ requests.
|
|
+ For example, the receipt of a single mail message can prompt
|
|
+ requests from an SMTP server for NS, PTR, A, and AAAA records
|
|
+ as the incoming SMTP/TCP/IP connection is considered.
|
|
+ The SMTP server can need additional NS, A, AAAA, MX, TXT, and SPF
|
|
+ records as it considers the STMP <command>Mail From</command>
|
|
+ command.
|
|
+ Web browsers often repeatedly resolve the same names that
|
|
+ are repeated in HTML <IMG> tags in a page.
|
|
+ <command>All-per-second</command> is similar to the
|
|
+ rate limiting offered by firewalls but often inferior.
|
|
+ Attacks that justify ignoring the
|
|
+ contents of DNS responses are likely to be attacks on the
|
|
+ DNS server itself.
|
|
+ They usually should be discarded before the DNS server
|
|
+ spends resources make TCP connections or parsing DNS requesets,
|
|
+ but that rate limiting must be done before the
|
|
+ DNS server sees the requests.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ The maximum size of the table used to track requests and
|
|
+ rate limit responses is set with <command>max-table-size</command>.
|
|
+ Each entry in the table is between 40 and 80 bytes.
|
|
+ The table needs approximately as many entries as the number
|
|
+ of requests received per second.
|
|
+ The default is 20,000.
|
|
+ To reduce the cold start of growing the table,
|
|
+ <command>min-table-size</command> (default 500)
|
|
+ can set the minimum table size.
|
|
+ Enable <command>rate-limit</command> category logging to monitor
|
|
+ expansions of the table and inform
|
|
+ choices for the initial and maximum table size.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Use <command>log-only yes</command> to test rate limiting parameters
|
|
+ without actually dropping any requests.
|
|
+ </para>
|
|
+
|
|
+ <para>
|
|
+ Responses dropped by rate limits are included in the
|
|
+ <command>RateDropped</command> and <command>QryDropped</command>
|
|
+ statistics.
|
|
+ Responses that truncated by rate limits are included in
|
|
+ <command>RateSlipped</command> and <command>RespTruncated</command>.
|
|
+ </sect3>
|
|
</sect2>
|
|
|
|
<sect2 id="server_statement_grammar">
|
|
@@ -14649,6 +14946,32 @@
|
|
</para>
|
|
</entry>
|
|
</row>
|
|
+ <row rowsep="0">
|
|
+ <entry colname="1">
|
|
+ <para><command>RateDropped</command></para>
|
|
+ </entry>
|
|
+ <entry colname="2">
|
|
+ <para><command></command></para>
|
|
+ </entry>
|
|
+ <entry colname="3">
|
|
+ <para>
|
|
+ Responses dropped by rate limits.
|
|
+ </para>
|
|
+ </entry>
|
|
+ </row>
|
|
+ <row rowsep="0">
|
|
+ <entry colname="1">
|
|
+ <para><command>RateSlipped</command></para>
|
|
+ </entry>
|
|
+ <entry colname="2">
|
|
+ <para><command></command></para>
|
|
+ </entry>
|
|
+ <entry colname="3">
|
|
+ <para>
|
|
+ Responses truncated by rate limits.
|
|
+ </para>
|
|
+ </entry>
|
|
+ </row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
diff -r -u hints-orig hints
|
|
--- hints-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ hints 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,36 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+. 0 NS ns1.
|
|
+ns1. 0 A 10.53.0.1
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+. 0 NS ns1.
|
|
+ns1. 0 A 10.53.0.1
|
|
diff -r -u lib/dns/Makefile.in-orig lib/dns/Makefile.in
|
|
--- lib/dns/Makefile.in-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/Makefile.in 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -67,8 +67,8 @@
|
|
portlist.@O@ private.@O@ \
|
|
rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
|
|
rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \
|
|
- request.@O@ resolver.@O@ result.@O@ rootns.@O@ rpz.@O@ \
|
|
- rriterator.@O@ sdb.@O@ \
|
|
+ request.@O@ resolver.@O@ result.@O@ rootns.@O@ \
|
|
+ rpz.@O@ rrl.@O@ rriterator.@O@ sdb.@O@ \
|
|
sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \
|
|
stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
|
|
tsec.@O@ tsig.@O@ ttl.@O@ update.@O@ validator.@O@ \
|
|
@@ -95,7 +95,7 @@
|
|
name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
|
|
rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c rdatalist.c \
|
|
rdataset.c rdatasetiter.c rdataslab.c request.c \
|
|
- resolver.c result.c rootns.c rpz.c rriterator.c \
|
|
+ resolver.c result.c rootns.c rpz.c rrl.c rriterator.c \
|
|
sdb.c sdlz.c soa.c ssu.c ssu_external.c \
|
|
stats.c tcpmsg.c time.c timer.c tkey.c \
|
|
tsec.c tsig.c ttl.c update.c validator.c \
|
|
diff -r -u lib/dns/db.c-orig lib/dns/db.c
|
|
--- lib/dns/db.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/db.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1007,21 +1007,23 @@
|
|
(db->methods->resigned)(db, rdataset, version);
|
|
}
|
|
|
|
-isc_result_t
|
|
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
|
|
-{
|
|
- if (db->methods->rpz_enabled != NULL)
|
|
- return ((db->methods->rpz_enabled)(db, st));
|
|
- return (ISC_R_SUCCESS);
|
|
+/*
|
|
+ * 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_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));
|
|
}
|
|
diff -r -u lib/dns/ecdb.c-orig lib/dns/ecdb.c
|
|
--- lib/dns/ecdb.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/ecdb.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -582,8 +582,8 @@
|
|
NULL, /* resigned */
|
|
NULL, /* isdnssec */
|
|
NULL, /* getrrsetstats */
|
|
- NULL, /* rpz_enabled */
|
|
- NULL, /* rpz_findips */
|
|
+ NULL, /* rpz_attach */
|
|
+ NULL, /* rpz_ready */
|
|
NULL, /* findnodeext */
|
|
NULL /* findext */
|
|
};
|
|
diff -r -u lib/dns/include/dns/db.h-orig lib/dns/include/dns/db.h
|
|
--- lib/dns/include/dns/db.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/db.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -172,14 +172,9 @@
|
|
dns_dbversion_t *version);
|
|
isc_boolean_t (*isdnssec)(dns_db_t *db);
|
|
dns_stats_t *(*getrrsetstats)(dns_db_t *db);
|
|
- isc_result_t (*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,
|
|
@@ -1542,30 +1537,17 @@
|
|
* dns_rdatasetstats_create(); otherwise NULL.
|
|
*/
|
|
|
|
-isc_result_t
|
|
-dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st);
|
|
+void
|
|
+dns_db_rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
|
|
/*%<
|
|
- * Mark a database for response policy rewriting
|
|
- * or find which RPZ data is available.
|
|
+ * 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);
|
|
-/*%<
|
|
- * 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
|
|
+isc_result_t
|
|
+dns_db_rpz_ready(dns_db_t *db);
|
|
+/*%<
|
|
+ * Finish loading a response policy zone.
|
|
*/
|
|
|
|
ISC_LANG_ENDDECLS
|
|
diff -r -u lib/dns/include/dns/log.h-orig lib/dns/include/dns/log.h
|
|
--- lib/dns/include/dns/log.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/log.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -43,6 +43,7 @@
|
|
#define DNS_LOGCATEGORY_DELEGATION_ONLY (&dns_categories[10])
|
|
#define DNS_LOGCATEGORY_EDNS_DISABLED (&dns_categories[11])
|
|
#define DNS_LOGCATEGORY_RPZ (&dns_categories[12])
|
|
+#define DNS_LOGCATEGORY_RRL (&dns_categories[13])
|
|
|
|
/* Backwards compatibility. */
|
|
#define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL
|
|
diff -r -u lib/dns/include/dns/rpz.h-orig lib/dns/include/dns/rpz.h
|
|
--- lib/dns/include/dns/rpz.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/rpz.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -14,7 +14,6 @@
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-/* $Id$ */
|
|
|
|
|
|
#ifndef DNS_RPZ_H
|
|
@@ -25,19 +24,30 @@
|
|
#include <dns/fixedname.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/types.h>
|
|
+#include <isc/refcount.h>
|
|
|
|
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"
|
|
|
|
-typedef isc_uint8_t dns_rpz_cidr_bits_t;
|
|
+
|
|
+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,
|
|
@@ -45,45 +55,148 @@
|
|
} 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_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, /* '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_CNAME = 5, /* 'cname x': answer with x's rrsets */
|
|
+ DNS_RPZ_POLICY_DROP = 3, /* 'drop': do not respond */
|
|
+ DNS_RPZ_POLICY_NXDOMAIN = 4, /* 'nxdomain': answer with NXDOMAIN */
|
|
+ DNS_RPZ_POLICY_NODATA = 5, /* 'nodata': answer with ANCOUNT=0 */
|
|
+ DNS_RPZ_POLICY_CNAME = 6, /* 'cname x': answer with x's rrsets */
|
|
DNS_RPZ_POLICY_RECORD,
|
|
DNS_RPZ_POLICY_WILDCNAME,
|
|
DNS_RPZ_POLICY_MISS,
|
|
DNS_RPZ_POLICY_ERROR
|
|
} dns_rpz_policy_t;
|
|
|
|
+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))
|
|
|
|
+/*
|
|
+ * 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 nsdname;
|
|
+ int nsipv4;
|
|
+ int nsipv6;
|
|
+};
|
|
+/*
|
|
+ * A single response policy zone.
|
|
+ */
|
|
+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_boolean_t defined;
|
|
+ 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_NAME. */
|
|
+ dns_name_t drop; /* DNS_RPZ_DROP_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;
|
|
};
|
|
|
|
/*
|
|
- * Radix trees for response policy IP addresses.
|
|
+ * Radix tree node for response policy IP addresses
|
|
+ */
|
|
+typedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t;
|
|
+
|
|
+/*
|
|
+ * Response policy zones known to a view.
|
|
*/
|
|
-typedef struct dns_rpz_cidr dns_rpz_cidr_t;
|
|
+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 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 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
|
|
@@ -91,22 +204,19 @@
|
|
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_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 */
|
|
+# 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.
|
|
*/
|
|
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;
|
|
@@ -141,10 +251,15 @@
|
|
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;
|
|
@@ -171,32 +286,41 @@
|
|
const char *
|
|
dns_rpz_policy2str(dns_rpz_policy_t policy);
|
|
|
|
-void
|
|
-dns_rpz_cidr_free(dns_rpz_cidr_t **cidr);
|
|
-
|
|
-void
|
|
-dns_rpz_view_destroy(dns_view_t *view);
|
|
+dns_rpz_policy_t
|
|
+dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
|
|
+ dns_name_t *selfname);
|
|
|
|
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_get(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st);
|
|
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx);
|
|
|
|
void
|
|
-dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name);
|
|
+dns_rpz_attach_rpzs(dns_rpz_zones_t *source, dns_rpz_zones_t **target);
|
|
|
|
void
|
|
-dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name);
|
|
+dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp);
|
|
|
|
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_beginload(dns_rpz_zones_t **load_rpzsp,
|
|
+ dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num);
|
|
|
|
-dns_rpz_policy_t
|
|
-dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset,
|
|
- dns_name_t *selfname);
|
|
+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_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, dns_name_t *name);
|
|
+
|
|
+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_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
|
|
|
|
diff -r -u lib/dns/include/dns/rrl.h-orig lib/dns/include/dns/rrl.h
|
|
--- lib/dns/include/dns/rrl.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/rrl.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,278 @@
|
|
+/*
|
|
+ * Copyright (C) 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.
|
|
+ */
|
|
+
|
|
+
|
|
+#ifndef DNS_RRL_H
|
|
+#define DNS_RRL_H 1
|
|
+
|
|
+/*
|
|
+ * Rate limit DNS responses.
|
|
+ */
|
|
+
|
|
+#include <isc/lang.h>
|
|
+
|
|
+#include <dns/fixedname.h>
|
|
+#include <dns/rdata.h>
|
|
+#include <dns/types.h>
|
|
+
|
|
+ISC_LANG_BEGINDECLS
|
|
+
|
|
+
|
|
+/*
|
|
+ * Memory allocation or other failures.
|
|
+ */
|
|
+#define DNS_RRL_LOG_FAIL ISC_LOG_WARNING
|
|
+/*
|
|
+ * dropped or slipped responses.
|
|
+ */
|
|
+#define DNS_RRL_LOG_DROP ISC_LOG_INFO
|
|
+/*
|
|
+ * Major events in dropping or slipping.
|
|
+ */
|
|
+#define DNS_RRL_LOG_DEBUG1 ISC_LOG_DEBUG(3)
|
|
+/*
|
|
+ * Limit computations.
|
|
+ */
|
|
+#define DNS_RRL_LOG_DEBUG2 ISC_LOG_DEBUG(4)
|
|
+/*
|
|
+ * Even less interesting.
|
|
+ */
|
|
+#define DNS_RRL_LOG_DEBUG3 ISC_LOG_DEBUG(9)
|
|
+
|
|
+
|
|
+#define DNS_RRL_LOG_ERR_LEN 64
|
|
+#define DNS_RRL_LOG_BUF_LEN (sizeof("would continue limiting") + \
|
|
+ DNS_RRL_LOG_ERR_LEN + \
|
|
+ sizeof(" responses to ") + \
|
|
+ ISC_NETADDR_FORMATSIZE + \
|
|
+ sizeof("/128 for IN ") + \
|
|
+ DNS_RDATATYPE_FORMATSIZE + \
|
|
+ DNS_NAME_FORMATSIZE)
|
|
+
|
|
+
|
|
+typedef struct dns_rrl_hash dns_rrl_hash_t;
|
|
+
|
|
+/*
|
|
+ * Response types.
|
|
+ */
|
|
+typedef enum {
|
|
+ DNS_RRL_RTYPE_FREE = 0,
|
|
+ DNS_RRL_RTYPE_QUERY,
|
|
+ DNS_RRL_RTYPE_REFERRAL,
|
|
+ DNS_RRL_RTYPE_NODATA,
|
|
+ DNS_RRL_RTYPE_NXDOMAIN,
|
|
+ DNS_RRL_RTYPE_ERROR,
|
|
+ DNS_RRL_RTYPE_ALL,
|
|
+ DNS_RRL_RTYPE_TCP,
|
|
+} dns_rrl_rtype_t;
|
|
+
|
|
+/*
|
|
+ * A rate limit bucket key.
|
|
+ * This should be small to limit the total size of the database.
|
|
+ * The hash of the qname should be wide enough to make the probability
|
|
+ * of collisions among requests from a single IP address block less than 50%.
|
|
+ * We need a 32-bit hash value for 10000 qps (e.g. random qnames forged
|
|
+ * by attacker) to collide with legitimate qnames from the target with
|
|
+ * probability at most 1%.
|
|
+ */
|
|
+#define DNS_RRL_MAX_PREFIX 64
|
|
+typedef union dns_rrl_key dns_rrl_key_t;
|
|
+union dns_rrl_key {
|
|
+ struct {
|
|
+ isc_uint32_t ip[DNS_RRL_MAX_PREFIX/32];
|
|
+ isc_uint32_t qname_hash;
|
|
+ dns_rdatatype_t qtype;
|
|
+ isc_uint8_t qclass;
|
|
+ dns_rrl_rtype_t rtype :4; /* 3 bits + sign bit */
|
|
+ isc_boolean_t ipv6 :1;
|
|
+ } s;
|
|
+ isc_uint16_t w[1];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * A rate-limit entry.
|
|
+ * This should be small to limit the total size of the table of entries.
|
|
+ */
|
|
+typedef struct dns_rrl_entry dns_rrl_entry_t;
|
|
+typedef ISC_LIST(dns_rrl_entry_t) dns_rrl_bin_t;
|
|
+struct dns_rrl_entry {
|
|
+ ISC_LINK(dns_rrl_entry_t) lru;
|
|
+ ISC_LINK(dns_rrl_entry_t) hlink;
|
|
+ dns_rrl_key_t key;
|
|
+# define DNS_RRL_RESPONSE_BITS 24
|
|
+ signed int responses :DNS_RRL_RESPONSE_BITS;
|
|
+# define DNS_RRL_QNAMES_BITS 8
|
|
+ unsigned int log_qname :DNS_RRL_QNAMES_BITS;
|
|
+
|
|
+# define DNS_RRL_TS_GEN_BITS 2
|
|
+ unsigned int ts_gen :DNS_RRL_TS_GEN_BITS;
|
|
+ isc_boolean_t ts_valid :1;
|
|
+# define DNS_RRL_HASH_GEN_BITS 1
|
|
+ unsigned int hash_gen :DNS_RRL_HASH_GEN_BITS;
|
|
+ isc_boolean_t logged :1;
|
|
+# define DNS_RRL_LOG_BITS 11
|
|
+ unsigned int log_secs :DNS_RRL_LOG_BITS;
|
|
+
|
|
+# define DNS_RRL_TS_BITS 12
|
|
+ unsigned int ts :DNS_RRL_TS_BITS;
|
|
+
|
|
+# define DNS_RRL_MAX_SLIP 10
|
|
+ unsigned int slip_cnt :4;
|
|
+};
|
|
+
|
|
+#define DNS_RRL_MAX_TIME_TRAVEL 5
|
|
+#define DNS_RRL_FOREVER (1<<DNS_RRL_TS_BITS)
|
|
+#define DNS_RRL_MAX_TS (DNS_RRL_FOREVER - 1)
|
|
+
|
|
+#define DNS_RRL_MAX_RESPONSES ((1<<(DNS_RRL_RESPONSE_BITS-1))-1)
|
|
+#define DNS_RRL_MAX_WINDOW 3600
|
|
+#if DNS_RRL_MAX_WINDOW >= DNS_RRL_MAX_TS
|
|
+#error "DNS_RRL_MAX_WINDOW is too large"
|
|
+#endif
|
|
+#define DNS_RRL_MAX_RATE 1000
|
|
+#if DNS_RRL_MAX_RATE >= (DNS_RRL_MAX_RESPONSES / DNS_RRL_MAX_WINDOW)
|
|
+#error "DNS_RRL_MAX_rate is too large"
|
|
+#endif
|
|
+
|
|
+#if (1<<DNS_RRL_LOG_BITS) >= DNS_RRL_FOREVER
|
|
+#error DNS_RRL_LOG_BITS is too big
|
|
+#endif
|
|
+#define DNS_RRL_MAX_LOG_SECS 1800
|
|
+#if DNS_RRL_MAX_LOG_SECS >= (1<<DNS_RRL_LOG_BITS)
|
|
+#error "DNS_RRL_MAX_LOG_SECS is too large"
|
|
+#endif
|
|
+#define DNS_RRL_STOP_LOG_SECS 60
|
|
+#if DNS_RRL_STOP_LOG_SECS >= (1<<DNS_RRL_LOG_BITS)
|
|
+#error "DNS_RRL_STOP_LOG_SECS is too large"
|
|
+#endif
|
|
+
|
|
+
|
|
+/*
|
|
+ * A hash table of rate-limit entries.
|
|
+ */
|
|
+struct dns_rrl_hash {
|
|
+ isc_stdtime_t check_time;
|
|
+ unsigned int gen :DNS_RRL_HASH_GEN_BITS;
|
|
+ int length;
|
|
+ dns_rrl_bin_t bins[1];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * A block of rate-limit entries.
|
|
+ */
|
|
+typedef struct dns_rrl_block dns_rrl_block_t;
|
|
+struct dns_rrl_block {
|
|
+ ISC_LINK(dns_rrl_block_t) link;
|
|
+ int size;
|
|
+ dns_rrl_entry_t entries[1];
|
|
+};
|
|
+
|
|
+/*
|
|
+ * A rate limited qname buffer.
|
|
+ */
|
|
+typedef struct dns_rrl_qname_buf dns_rrl_qname_buf_t;
|
|
+struct dns_rrl_qname_buf {
|
|
+ ISC_LINK(dns_rrl_qname_buf_t) link;
|
|
+ const dns_rrl_entry_t *e;
|
|
+ unsigned int index;
|
|
+ dns_fixedname_t qname;
|
|
+};
|
|
+
|
|
+typedef struct dns_rrl_rate dns_rrl_rate_t;
|
|
+struct dns_rrl_rate {
|
|
+ int r;
|
|
+ int scaled;
|
|
+ const char *str;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Per-view query rate limit parameters and a pointer to database.
|
|
+ */
|
|
+typedef struct dns_rrl dns_rrl_t;
|
|
+struct dns_rrl {
|
|
+ isc_mutex_t lock;
|
|
+ isc_mem_t *mctx;
|
|
+
|
|
+ isc_boolean_t log_only;
|
|
+ dns_rrl_rate_t responses_per_second;
|
|
+ dns_rrl_rate_t referrals_per_second;
|
|
+ dns_rrl_rate_t nodata_per_second;
|
|
+ dns_rrl_rate_t nxdomains_per_second;
|
|
+ dns_rrl_rate_t errors_per_second;
|
|
+ dns_rrl_rate_t all_per_second;
|
|
+ dns_rrl_rate_t slip;
|
|
+ int window;
|
|
+ double qps_scale;
|
|
+ int max_entries;
|
|
+
|
|
+ dns_acl_t *exempt;
|
|
+
|
|
+ int num_entries;
|
|
+
|
|
+ int qps_responses;
|
|
+ isc_stdtime_t qps_time;
|
|
+ double qps;
|
|
+
|
|
+ unsigned int probes;
|
|
+ unsigned int searches;
|
|
+
|
|
+ ISC_LIST(dns_rrl_block_t) blocks;
|
|
+ ISC_LIST(dns_rrl_entry_t) lru;
|
|
+
|
|
+ dns_rrl_hash_t *hash;
|
|
+ dns_rrl_hash_t *old_hash;
|
|
+ unsigned int hash_gen;
|
|
+
|
|
+ unsigned int ts_gen;
|
|
+# define DNS_RRL_TS_BASES (1<<DNS_RRL_TS_GEN_BITS)
|
|
+ isc_stdtime_t ts_bases[DNS_RRL_TS_BASES];
|
|
+
|
|
+ int ipv4_prefixlen;
|
|
+ isc_uint32_t ipv4_mask;
|
|
+ int ipv6_prefixlen;
|
|
+ isc_uint32_t ipv6_mask[4];
|
|
+
|
|
+ isc_stdtime_t log_stops_time;
|
|
+ dns_rrl_entry_t *last_logged;
|
|
+ int num_logged;
|
|
+ int num_qnames;
|
|
+ ISC_LIST(dns_rrl_qname_buf_t) qname_free;
|
|
+# define DNS_RRL_QNAMES (1<<DNS_RRL_QNAMES_BITS)
|
|
+ dns_rrl_qname_buf_t *qnames[DNS_RRL_QNAMES];
|
|
+};
|
|
+
|
|
+typedef enum {
|
|
+ DNS_RRL_RESULT_OK,
|
|
+ DNS_RRL_RESULT_DROP,
|
|
+ DNS_RRL_RESULT_SLIP,
|
|
+} dns_rrl_result_t;
|
|
+
|
|
+dns_rrl_result_t
|
|
+dns_rrl(dns_view_t *view,
|
|
+ const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp,
|
|
+ dns_rdataclass_t rdclass, dns_rdatatype_t qtype,
|
|
+ dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
|
|
+ isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len);
|
|
+
|
|
+void
|
|
+dns_rrl_view_destroy(dns_view_t *view);
|
|
+
|
|
+isc_result_t
|
|
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries);
|
|
+
|
|
+ISC_LANG_ENDDECLS
|
|
+
|
|
+#endif /* DNS_RRL_H */
|
|
diff -r -u lib/dns/include/dns/view.h-orig lib/dns/include/dns/view.h
|
|
--- lib/dns/include/dns/view.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/view.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -73,6 +73,7 @@
|
|
|
|
#include <dns/acl.h>
|
|
#include <dns/fixedname.h>
|
|
+#include <dns/rrl.h>
|
|
#include <dns/rdatastruct.h>
|
|
#include <dns/rpz.h>
|
|
#include <dns/types.h>
|
|
@@ -142,6 +143,7 @@
|
|
dns_rbt_t * answeracl_exclude;
|
|
dns_rbt_t * denyanswernames;
|
|
dns_rbt_t * answernames_exclude;
|
|
+ dns_rrl_t * rrl;
|
|
isc_boolean_t provideixfr;
|
|
isc_boolean_t requestnsid;
|
|
dns_ttl_t maxcachettl;
|
|
@@ -162,10 +164,7 @@
|
|
dns_acl_t * v4_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;
|
|
- unsigned int rpz_min_ns_labels;
|
|
+ dns_rpz_zones_t *rpzs;
|
|
|
|
/*
|
|
* Configurable data for server use only,
|
|
diff -r -u lib/dns/include/dns/zone.h-orig lib/dns/include/dns/zone.h
|
|
--- lib/dns/include/dns/zone.h-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/include/dns/zone.h 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -2081,13 +2081,14 @@
|
|
*/
|
|
|
|
isc_result_t
|
|
-dns_zone_rpz_enable(dns_zone_t *zone);
|
|
+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.
|
|
*/
|
|
|
|
-isc_boolean_t
|
|
-dns_zone_get_rpz(dns_zone_t *zone);
|
|
+dns_rpz_num_t
|
|
+dns_zone_get_rpz_num(dns_zone_t *zone);
|
|
|
|
void
|
|
dns_zone_setstatlevel(dns_zone_t *zone, dns_zonestat_level_t level);
|
|
diff -r -u lib/dns/log.c-orig lib/dns/log.c
|
|
--- lib/dns/log.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/log.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -45,6 +45,7 @@
|
|
{ "delegation-only", 0 },
|
|
{ "edns-disabled", 0 },
|
|
{ "rpz", 0 },
|
|
+ { "rate-limit", 0 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
diff -r -u lib/dns/rbtdb.c-orig lib/dns/rbtdb.c
|
|
--- lib/dns/rbtdb.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/rbtdb.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -453,7 +453,9 @@
|
|
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;
|
|
@@ -972,8 +974,18 @@
|
|
dns_stats_detach(&rbtdb->rrsetstats);
|
|
|
|
#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,
|
|
@@ -1515,11 +1527,11 @@
|
|
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);
|
|
@@ -1555,11 +1567,11 @@
|
|
isc_result_totext(result));
|
|
}
|
|
}
|
|
+ result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
|
|
#ifdef BIND9
|
|
- if (rbtdb->rpz_cidr != NULL)
|
|
- dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
|
|
+ if (rbtdb->rpzs != NULL)
|
|
+ dns_rpz_delete(rbtdb->rpzs, rbtdb->rpz_num, name);
|
|
#endif
|
|
- result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
|
|
break;
|
|
case DNS_RBT_NSEC_NSEC:
|
|
result = dns_rbt_deletenode(rbtdb->nsec, node, ISC_FALSE);
|
|
@@ -1573,7 +1585,7 @@
|
|
DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_CACHE,
|
|
ISC_LOG_WARNING,
|
|
- "delete_cnode(): "
|
|
+ "delete_node(): "
|
|
"dns_rbt_deletenode: %s",
|
|
isc_result_totext(result));
|
|
}
|
|
@@ -2538,14 +2550,15 @@
|
|
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);
|
|
@@ -4547,228 +4560,45 @@
|
|
return (result);
|
|
}
|
|
|
|
+#ifdef BIND9
|
|
/*
|
|
- * Mark a database for response policy rewriting
|
|
- * or find which RPZ data is available.
|
|
+ * Connect this RBTDB to the response policy zone summary data for the view.
|
|
*/
|
|
-#ifdef BIND9
|
|
-static isc_result_t
|
|
-rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
|
|
-{
|
|
- dns_rbtdb_t *rbtdb;
|
|
- isc_result_t result;
|
|
+static void
|
|
+rpz_attach(dns_db_t *db, dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num) {
|
|
+ dns_rbtdb_t * rbtdb;
|
|
|
|
- result = ISC_R_SUCCESS;
|
|
rbtdb = (dns_rbtdb_t *)db;
|
|
REQUIRE(VALID_RBTDB(rbtdb));
|
|
- RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
|
|
- if (st != NULL) {
|
|
- dns_rpz_enabled_get(rbtdb->rpz_cidr, st);
|
|
- } else {
|
|
- result = dns_rpz_new_cidr(rbtdb->common.mctx,
|
|
- &rbtdb->common.origin,
|
|
- &rbtdb->rpz_cidr);
|
|
- }
|
|
- RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
|
|
- return (result);
|
|
+
|
|
+ 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
|
|
|
|
@@ -6874,8 +6704,9 @@
|
|
noderesult = dns_rbt_addnode(rbtdb->tree, name, nodep);
|
|
|
|
#ifdef BIND9
|
|
- if (noderesult == ISC_R_SUCCESS && rbtdb->rpz_cidr != NULL)
|
|
- 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)
|
|
@@ -7060,6 +6891,20 @@
|
|
|
|
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;
|
|
@@ -7461,8 +7306,8 @@
|
|
isdnssec,
|
|
NULL,
|
|
#ifdef BIND9
|
|
- rpz_enabled,
|
|
- rpz_findips,
|
|
+ rpz_attach,
|
|
+ rpz_ready,
|
|
#else
|
|
NULL,
|
|
NULL,
|
|
@@ -7776,6 +7621,9 @@
|
|
}
|
|
rbtdb->attributes = 0;
|
|
rbtdb->task = NULL;
|
|
+ rbtdb->rpzs = NULL;
|
|
+ rbtdb->load_rpzs = NULL;
|
|
+ rbtdb->rpz_num = DNS_RPZ_INVALID_NUM;
|
|
|
|
/*
|
|
* Version Initialization.
|
|
diff -r -u lib/dns/rpz.c-orig lib/dns/rpz.c
|
|
--- lib/dns/rpz.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/rpz.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -37,6 +37,7 @@
|
|
#include <dns/rdataset.h>
|
|
#include <dns/rdatastruct.h>
|
|
#include <dns/result.h>
|
|
+#include <dns/rbt.h>
|
|
#include <dns/rpz.h>
|
|
#include <dns/view.h>
|
|
|
|
@@ -44,9 +45,13 @@
|
|
/*
|
|
* 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
|
|
@@ -55,7 +60,8 @@
|
|
* 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
|
|
@@ -72,7 +78,7 @@
|
|
* 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.
|
|
*/
|
|
|
|
@@ -90,43 +96,89 @@
|
|
} 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))))
|
|
+
|
|
+/*
|
|
+ * 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_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_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 triplet of arrays of bits flagging the existence of
|
|
+ * client-IP, IP, and NSIP policy triggers.
|
|
+ */
|
|
+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;
|
|
+};
|
|
|
|
-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_addr_zbits_t set;
|
|
+ dns_rpz_addr_zbits_t sum;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+/*
|
|
+ * 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;
|
|
};
|
|
|
|
-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 */
|
|
+typedef struct dns_rpz_nm_data dns_rpz_nm_data_t;
|
|
+struct dns_rpz_nm_data {
|
|
+ dns_rpz_nm_zbits_t set;
|
|
+ dns_rpz_nm_zbits_t wild;
|
|
};
|
|
|
|
+#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) {
|
|
switch (type) {
|
|
+ case DNS_RPZ_TYPE_CLIENT_IP:
|
|
+ return ("CLIENT-IP");
|
|
case DNS_RPZ_TYPE_QNAME:
|
|
return ("QNAME");
|
|
case DNS_RPZ_TYPE_IP:
|
|
@@ -138,32 +190,33 @@
|
|
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");
|
|
}
|
|
|
|
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},
|
|
+ {"nxdomain", DNS_RPZ_POLICY_NXDOMAIN},
|
|
+ {"nodata", DNS_RPZ_POLICY_NODATA},
|
|
+ {"cname", DNS_RPZ_POLICY_CNAME},
|
|
+ {"no-op", DNS_RPZ_POLICY_PASSTHRU}, /* Obsolete */
|
|
+ };
|
|
+ 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);
|
|
}
|
|
|
|
@@ -175,6 +228,9 @@
|
|
case DNS_RPZ_POLICY_PASSTHRU:
|
|
str = "PASSTHRU";
|
|
break;
|
|
+ case DNS_RPZ_POLICY_DROP:
|
|
+ str = "DROP";
|
|
+ break;
|
|
case DNS_RPZ_POLICY_NXDOMAIN:
|
|
str = "NXDOMAIN";
|
|
break;
|
|
@@ -196,243 +252,276 @@
|
|
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;
|
|
- }
|
|
+static int
|
|
+zbit_to_num(dns_rpz_zbits_t zbit) {
|
|
+ dns_rpz_num_t rpz_num;
|
|
|
|
- /* 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;
|
|
+ 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;
|
|
+#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);
|
|
}
|
|
|
|
/*
|
|
- * Forget a view's list of policy zones.
|
|
+ * Make a set of bit masks given one or more bits and their type.
|
|
*/
|
|
-void
|
|
-dns_rpz_view_destroy(dns_view_t *view) {
|
|
- dns_rpz_zone_t *zone;
|
|
-
|
|
- REQUIRE(view != NULL);
|
|
+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_CLIENT_IP:
|
|
+ tgt_set->client_ip = zbits;
|
|
+ tgt_set->ip = 0;
|
|
+ tgt_set->nsip = 0;
|
|
+ break;
|
|
+ case DNS_RPZ_TYPE_IP:
|
|
+ tgt_set->client_ip = 0;
|
|
+ tgt_set->ip = zbits;
|
|
+ tgt_set->nsip = 0;
|
|
+ break;
|
|
+ case DNS_RPZ_TYPE_NSIP:
|
|
+ tgt_set->client_ip = 0;
|
|
+ tgt_set->ip = 0;
|
|
+ tgt_set->nsip = zbits;
|
|
+ break;
|
|
+ default:
|
|
+ INSIST(0);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
|
|
- 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));
|
|
+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);
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
- * Start a new radix tree for a response policy zone.
|
|
+ * Mark a node and all of its parents as having client-IP, 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);
|
|
-
|
|
- 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_addr_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->set;
|
|
|
|
- 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.client_ip |= child->sum.client_ip;
|
|
+ sum.ip |= child->sum.ip;
|
|
+ sum.nsip |= child->sum.nsip;
|
|
+ }
|
|
|
|
- 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.client_ip |= child->sum.client_ip;
|
|
+ sum.ip |= child->sum.ip;
|
|
+ sum.nsip |= child->sum.nsip;
|
|
+ }
|
|
|
|
- *rbtdb_cidr = cidr;
|
|
- return (ISC_R_SUCCESS);
|
|
+ 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;
|
|
+ } while (cnode != NULL);
|
|
}
|
|
|
|
-/*
|
|
- * See if a policy zone has IP, NSIP, or NSDNAME rules or records.
|
|
- */
|
|
-void
|
|
-dns_rpz_enabled_get(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 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);
|
|
+static void
|
|
+fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) {
|
|
+ dns_rpz_zbits_t zbits;
|
|
+
|
|
+ /*
|
|
+ * 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.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;
|
|
}
|
|
|
|
-/*
|
|
- * 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;
|
|
+
|
|
+ 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 = &rpz->triggers.client_ipv4;
|
|
+ have = &rpzs->have.client_ipv4;
|
|
+ } else {
|
|
+ cnt = &rpz->triggers.client_ipv6;
|
|
+ have = &rpzs->have.client_ipv6;
|
|
+ }
|
|
+ break;
|
|
+ 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);
|
|
+ }
|
|
|
|
- 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;
|
|
+ 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));
|
|
+ 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,
|
|
"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
|
|
@@ -440,22 +529,18 @@
|
|
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++) {
|
|
@@ -469,9 +554,9 @@
|
|
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++]);
|
|
@@ -495,48 +580,31 @@
|
|
}
|
|
}
|
|
|
|
- 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);
|
|
|
|
- /*
|
|
- * 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, &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
|
|
|
|
@@ -545,73 +613,80 @@
|
|
|
|
/*
|
|
* 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_addr_zbits_t *new_set)
|
|
{
|
|
- 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_addr_set(new_set, 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;
|
|
+ dns_name_format(&ip_name, ip_str, sizeof(ip_str));
|
|
+ end = &ip_str[strlen(ip_str)+1];
|
|
+ prefix_str = ip_str;
|
|
|
|
- prefix = strtoul(prefix_str, &cp2, 10);
|
|
+ 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;
|
|
@@ -621,7 +696,7 @@
|
|
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);
|
|
}
|
|
@@ -632,7 +707,7 @@
|
|
/*
|
|
* 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--) {
|
|
@@ -651,7 +726,7 @@
|
|
(*cp2 != '.' && *cp2 != '\0')) {
|
|
if (*cp2 == '.')
|
|
*cp2 = '\0';
|
|
- badname(level, src_name,
|
|
+ badname(log_level, src_name,
|
|
"; invalid IPv6 word ", cp);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
@@ -665,36 +740,37 @@
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
@@ -702,10 +778,54 @@
|
|
}
|
|
|
|
/*
|
|
- * 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)
|
|
+{
|
|
+ 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;
|
|
+ memset(&new_data->set, 0, sizeof(new_data->set));
|
|
+ make_nm_set(&new_data->wild, rpz_num, rpz_type);
|
|
+ } else {
|
|
+ prefix_len = 0;
|
|
+ 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);
|
|
+ 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;
|
|
@@ -731,17 +851,17 @@
|
|
}
|
|
|
|
/*
|
|
- * 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
|
|
@@ -751,7 +871,7 @@
|
|
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;
|
|
}
|
|
}
|
|
@@ -759,133 +879,170 @@
|
|
}
|
|
|
|
/*
|
|
+ * 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 + 1);
|
|
+ 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_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_addr_zbits_t set;
|
|
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);
|
|
-
|
|
+ set = *tgt_set;
|
|
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->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);
|
|
}
|
|
|
|
- /*
|
|
- * 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.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.
|
|
+ * 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->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.
|
|
+ */
|
|
+ *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->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;
|
|
}
|
|
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->set = *tgt_set;
|
|
+ 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->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.
|
|
+ * Continue searching for other hits in the
|
|
+ * same or lower numbered trees.
|
|
+ */
|
|
find_result = DNS_R_PARTIALMATCH;
|
|
- if (found != NULL)
|
|
- *found = cur;
|
|
+ *found = cur;
|
|
+ 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);
|
|
cur = cur->child[cur_num];
|
|
@@ -894,7 +1051,7 @@
|
|
|
|
|
|
/*
|
|
- * 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
|
|
@@ -902,17 +1059,17 @@
|
|
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);
|
|
@@ -920,129 +1077,675 @@
|
|
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->set = *tgt_set;
|
|
+ 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;
|
|
-
|
|
- REQUIRE(cidr != NULL);
|
|
+ dns_rpz_prefix_t tgt_prefix;
|
|
+ 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, &set);
|
|
/*
|
|
- * 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, &set, ISC_TRUE, &found);
|
|
+ if (result != ISC_R_SUCCESS) {
|
|
+ char namebuf[DNS_NAME_FORMATSIZE];
|
|
|
|
/*
|
|
* bin/tests/system/rpz/tests.sh looks for "rpz.*failed".
|
|
*/
|
|
- dns_name_format(name, printname, sizeof(printname));
|
|
+ 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->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];
|
|
+
|
|
/*
|
|
- * We cannot easily count nsdnames because
|
|
- * internal rbt nodes get deleted.
|
|
+ * 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,
|
|
+ "rpz add_nm(%s): bits already set", namebuf);
|
|
+ return (ISC_R_EXISTS);
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+}
|
|
+
|
|
+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) {
|
|
+ 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;
|
|
- case DNS_RPZ_TYPE_QNAME:
|
|
- case DNS_RPZ_TYPE_BAD:
|
|
- return;
|
|
+ isc_refcount_destroy(&rpz->refs);
|
|
+
|
|
+ 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->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->drop))
|
|
+ dns_name_free(&rpz->drop, 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);
|
|
+
|
|
+ /*
|
|
+ * 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
|
|
+ */
|
|
+ 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(client_ipv4);
|
|
+ SET_TRIG(client_ipv6);
|
|
+ SET_TRIG(qname);
|
|
+ SET_TRIG(ipv4);
|
|
+ SET_TRIG(ipv6);
|
|
+ SET_TRIG(nsdname);
|
|
+ SET_TRIG(nsipv4);
|
|
+ SET_TRIG(nsipv6);
|
|
+ }
|
|
+
|
|
+ fix_qname_skip_recurse(rpzs);
|
|
+
|
|
+# undef SET_TRIG
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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_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;
|
|
+
|
|
+ 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 client-IPv4, %d client-IPv6,"
|
|
+ " %d qname, %d nsdname,"
|
|
+ " %d IPv4, %d IPv6,"
|
|
+ " %d NSIPv4, %d NSIPv6 entries",
|
|
+ rpzs->p.num_zones,
|
|
+ new_totals.client_ipv4,
|
|
+ new_totals.client_ipv6,
|
|
+ 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) {
|
|
+ new_bit = ~DNS_RPZ_ZBIT(rpz_num);
|
|
+
|
|
+ /*
|
|
+ * Copy to the radix tree.
|
|
+ */
|
|
+ for (cnode = rpzs->cidr; cnode != NULL; cnode = next_cnode) {
|
|
+ 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_ip, ISC_TRUE, &found);
|
|
+ if (result == ISC_R_NOMEMORY)
|
|
+ goto unlock_and_detach;
|
|
+ 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 branch 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.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);
|
|
+ INSIST(result == ISC_R_SUCCESS);
|
|
+ result = add_nm(load_rpzs, name,
|
|
+ &new_data);
|
|
+ if (result != ISC_R_SUCCESS)
|
|
+ goto unlock_and_detach;
|
|
+ }
|
|
+ }
|
|
+ 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));
|
|
+ goto unlock_and_detach;
|
|
+ }
|
|
}
|
|
|
|
+ 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);
|
|
+
|
|
/*
|
|
- * Do not get excited about the deletion of interior rbt nodes.
|
|
+ * Exchange the summary databases.
|
|
*/
|
|
- result = name2ipkey(cidr, DNS_RPZ_DEBUG_QUIET, name,
|
|
- type, &tgt_ip, &tgt_prefix);
|
|
+ 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);
|
|
+ result = ISC_R_SUCCESS;
|
|
+
|
|
+ unlock_and_detach:
|
|
+ UNLOCK(&rpzs->maint_lock);
|
|
+ UNLOCK(&load_rpzs->search_lock);
|
|
+ UNLOCK(&load_rpzs->maint_lock);
|
|
+ dns_rpz_detach_rpzs(load_rpzsp);
|
|
+ return (result);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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 = ISC_R_FAILURE;
|
|
+
|
|
+ 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_CLIENT_IP:
|
|
+ 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_addr_zbits_t tgt_set;
|
|
+ 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, &tgt_set);
|
|
if (result != ISC_R_SUCCESS)
|
|
return;
|
|
|
|
- result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_FALSE, &tgt);
|
|
+ result = search(rpzs, &tgt_ip, tgt_prefix, &tgt_set, ISC_FALSE, &tgt);
|
|
if (result != ISC_R_SUCCESS) {
|
|
- badname(DNS_RPZ_ERROR_LEVEL, name, "; missing rpz node", "");
|
|
+ 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.
|
|
*/
|
|
- 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_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);
|
|
|
|
/*
|
|
* We might need to delete 2 nodes.
|
|
@@ -1054,13 +1757,14 @@
|
|
*/
|
|
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->set.client_ip != 0 ||
|
|
+ tgt->set.ip != 0 ||
|
|
+ tgt->set.nsip != 0)
|
|
+ break;
|
|
|
|
/*
|
|
* Replace the pointer to this node in the parent with
|
|
@@ -1068,7 +1772,7 @@
|
|
*/
|
|
parent = tgt->parent;
|
|
if (parent == NULL) {
|
|
- cidr->root = child;
|
|
+ rpzs->cidr = child;
|
|
} else {
|
|
parent->child[parent->child[1] == tgt] = child;
|
|
}
|
|
@@ -1077,26 +1781,144 @@
|
|
*/
|
|
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.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->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->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) {
|
|
+ /*
|
|
+ * 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_CLIENT_IP:
|
|
+ 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_addr_zbits_t tgt_set;
|
|
dns_rpz_cidr_node_t *found;
|
|
+ isc_result_t result;
|
|
+ dns_rpz_num_t rpz_num;
|
|
int i;
|
|
|
|
/*
|
|
@@ -1107,29 +1929,163 @@
|
|
tgt_ip.w[1] = 0;
|
|
tgt_ip.w[2] = ADDR_V4MAPPED;
|
|
tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr);
|
|
+ switch (rpz_type) {
|
|
+ case DNS_RPZ_TYPE_CLIENT_IP:
|
|
+ zbits &= rpzs->have.client_ipv4;
|
|
+ break;
|
|
+ case DNS_RPZ_TYPE_IP:
|
|
+ zbits &= rpzs->have.ipv4;
|
|
+ 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;
|
|
|
|
/*
|
|
* 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]);
|
|
}
|
|
+ switch (rpz_type) {
|
|
+ case DNS_RPZ_TYPE_CLIENT_IP:
|
|
+ zbits &= rpzs->have.client_ipv6;
|
|
+ break;
|
|
+ case DNS_RPZ_TYPE_IP:
|
|
+ zbits &= rpzs->have.ipv6;
|
|
+ break;
|
|
+ case DNS_RPZ_TYPE_NSIP:
|
|
+ zbits &= rpzs->have.nsipv6;
|
|
+ break;
|
|
+ default:
|
|
+ INSIST(0);
|
|
+ break;
|
|
+ }
|
|
} 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_addr_set(&tgt_set, zbits, rpz_type);
|
|
+
|
|
+ LOCK(&rpzs->search_lock);
|
|
+ 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.
|
|
+ */
|
|
+ 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;
|
|
+ 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);
|
|
+ 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);
|
|
+}
|
|
|
|
- *prefix = found->bits;
|
|
- return (ip2name(cidr, &found->ip, found->bits, type,
|
|
- canon_name, search_name));
|
|
+/*
|
|
+ * 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->set.qname;
|
|
+ else
|
|
+ found_zbits = nm_data->set.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.qname;
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ UNLOCK(&rpzs->search_lock);
|
|
+ return (zbits & found_zbits);
|
|
}
|
|
|
|
/*
|
|
@@ -1144,10 +2100,10 @@
|
|
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);
|
|
|
|
/*
|
|
@@ -1174,7 +2130,13 @@
|
|
}
|
|
|
|
/*
|
|
- * CNAME PASSTHRU.origin means "do not rewrite.
|
|
+ * CNAME rpz-drop. means "do 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);
|
|
diff -r -u lib/dns/rrl.c-orig lib/dns/rrl.c
|
|
--- lib/dns/rrl.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/rrl.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,1324 @@
|
|
+/*
|
|
+ * Copyright (C) 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.
|
|
+ */
|
|
+
|
|
+/*! \file */
|
|
+
|
|
+/*
|
|
+ * Rate limit DNS responses.
|
|
+ */
|
|
+
|
|
+/* #define ISC_LIST_CHECKINIT */
|
|
+
|
|
+#include <config.h>
|
|
+#include <isc/mem.h>
|
|
+#include <isc/net.h>
|
|
+#include <isc/netaddr.h>
|
|
+#include <isc/print.h>
|
|
+
|
|
+#include <dns/result.h>
|
|
+#include <dns/rcode.h>
|
|
+#include <dns/rdatatype.h>
|
|
+#include <dns/rdataclass.h>
|
|
+#include <dns/log.h>
|
|
+#include <dns/rrl.h>
|
|
+#include <dns/view.h>
|
|
+
|
|
+static void
|
|
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early,
|
|
+ char *log_buf, unsigned int log_buf_len);
|
|
+
|
|
+/*
|
|
+ * Get a modulus for a hash function that is tolerably likely to be
|
|
+ * relatively prime to most inputs. Of course, we get a prime for for initial
|
|
+ * values not larger than the square of the last prime. We often get a prime
|
|
+ * after that.
|
|
+ * This works well in practice for hash tables up to at least 100
|
|
+ * times the square of the last prime and better than a multiplicative hash.
|
|
+ */
|
|
+static int
|
|
+hash_divisor(unsigned int initial) {
|
|
+ static isc_uint16_t primes[] = {
|
|
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
|
|
+ 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
|
+#if 0
|
|
+ 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157,
|
|
+ 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
|
|
+ 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
|
|
+ 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367,
|
|
+ 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439,
|
|
+ 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
|
|
+ 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599,
|
|
+ 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
|
|
+ 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751,
|
|
+ 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
|
|
+ 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919,
|
|
+ 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,1009,
|
|
+#endif
|
|
+ };
|
|
+ int divisions, tries;
|
|
+ unsigned int result;
|
|
+ isc_uint16_t *pp, p;
|
|
+
|
|
+ result = initial;
|
|
+
|
|
+ if (primes[sizeof(primes)/sizeof(primes[0])-1] >= result) {
|
|
+ pp = primes;
|
|
+ while (*pp < result)
|
|
+ ++pp;
|
|
+ return (*pp);
|
|
+ }
|
|
+
|
|
+ if ((result & 1) == 0)
|
|
+ ++result;
|
|
+
|
|
+ divisions = 0;
|
|
+ tries = 1;
|
|
+ pp = primes;
|
|
+ do {
|
|
+ p = *pp++;
|
|
+ ++divisions;
|
|
+ if ((result % p) == 0) {
|
|
+ ++tries;
|
|
+ result += 2;
|
|
+ pp = primes;
|
|
+ }
|
|
+ } while (pp < &primes[sizeof(primes) / sizeof(primes[0])]);
|
|
+
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
|
|
+ "%d hash_divisor() divisions in %d tries"
|
|
+ " to get %d from %d",
|
|
+ divisions, tries, result, initial);
|
|
+
|
|
+ return (result);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert a timestamp to a number of seconds in the past.
|
|
+ */
|
|
+static inline int
|
|
+delta_rrl_time(isc_stdtime_t ts, isc_stdtime_t now) {
|
|
+ int delta;
|
|
+
|
|
+ delta = now - ts;
|
|
+ if (delta >= 0)
|
|
+ return (delta);
|
|
+
|
|
+ /*
|
|
+ * The timestamp is in the future. That future might result from
|
|
+ * re-ordered requests, because we use timestamps on requests
|
|
+ * instead of consulting a clock. Timestamps in the distant future are
|
|
+ * assumed to result from clock changes. When the clock changes to
|
|
+ * the past, make existing timestamps appear to be in the past.
|
|
+ */
|
|
+ if (delta < -DNS_RRL_MAX_TIME_TRAVEL)
|
|
+ return (DNS_RRL_FOREVER);
|
|
+ return (0);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+get_age(const dns_rrl_t *rrl, const dns_rrl_entry_t *e, isc_stdtime_t now) {
|
|
+ if (!e->ts_valid)
|
|
+ return (DNS_RRL_FOREVER);
|
|
+ return (delta_rrl_time(e->ts + rrl->ts_bases[e->ts_gen], now));
|
|
+}
|
|
+
|
|
+static inline void
|
|
+set_age(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_stdtime_t now) {
|
|
+ dns_rrl_entry_t *e_old;
|
|
+ unsigned int ts_gen;
|
|
+ int i, ts;
|
|
+
|
|
+ ts_gen = rrl->ts_gen;
|
|
+ ts = now - rrl->ts_bases[ts_gen];
|
|
+ if (ts < 0) {
|
|
+ if (ts < -DNS_RRL_MAX_TIME_TRAVEL)
|
|
+ ts = DNS_RRL_FOREVER;
|
|
+ else
|
|
+ ts = 0;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Make a new timestamp base if the current base is too old.
|
|
+ * All entries older than DNS_RRL_MAX_WINDOW seconds are ancient,
|
|
+ * useless history. Their timestamps can be treated as if they are
|
|
+ * all the same.
|
|
+ * We only do arithmetic on more recent timestamps, so bases for
|
|
+ * older timestamps can be recycled provided the old timestamps are
|
|
+ * marked as ancient history.
|
|
+ * This loop is almost always very short because most entries are
|
|
+ * recycled after one second and any entries that need to be marked
|
|
+ * are older than (DNS_RRL_TS_BASES)*DNS_RRL_MAX_TS seconds.
|
|
+ */
|
|
+ if (ts >= DNS_RRL_MAX_TS) {
|
|
+ ts_gen = (ts_gen + 1) % DNS_RRL_TS_BASES;
|
|
+ for (e_old = ISC_LIST_TAIL(rrl->lru), i = 0;
|
|
+ e_old != NULL && (e_old->ts_gen == ts_gen ||
|
|
+ !ISC_LINK_LINKED(e_old, hlink));
|
|
+ e_old = ISC_LIST_PREV(e_old, lru), ++i)
|
|
+ {
|
|
+ e_old->ts_valid = ISC_FALSE;
|
|
+ }
|
|
+ if (i != 0)
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
|
|
+ "rrl new time base scanned %d entries"
|
|
+ " at %d for %d %d %d %d",
|
|
+ i, now, rrl->ts_bases[ts_gen],
|
|
+ rrl->ts_bases[(ts_gen + 1) %
|
|
+ DNS_RRL_TS_BASES],
|
|
+ rrl->ts_bases[(ts_gen + 2) %
|
|
+ DNS_RRL_TS_BASES],
|
|
+ rrl->ts_bases[(ts_gen + 3) %
|
|
+ DNS_RRL_TS_BASES]);
|
|
+ rrl->ts_gen = ts_gen;
|
|
+ rrl->ts_bases[ts_gen] = now;
|
|
+ ts = 0;
|
|
+ }
|
|
+
|
|
+ e->ts_gen = ts_gen;
|
|
+ e->ts = ts;
|
|
+ e->ts_valid = ISC_TRUE;
|
|
+}
|
|
+
|
|
+static isc_result_t
|
|
+expand_entries(dns_rrl_t *rrl, int new) {
|
|
+ unsigned int bsize;
|
|
+ dns_rrl_block_t *b;
|
|
+ dns_rrl_entry_t *e;
|
|
+ double rate;
|
|
+ int i;
|
|
+
|
|
+ if (rrl->num_entries+new >= rrl->max_entries && rrl->max_entries != 0) {
|
|
+ if (rrl->num_entries >= rrl->max_entries)
|
|
+ return (ISC_R_SUCCESS);
|
|
+ new = rrl->max_entries - rrl->num_entries;
|
|
+ if (new <= 0)
|
|
+ return (ISC_R_NOMEMORY);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Log expansions so that the user can tune max-table-size
|
|
+ * and min-table-size.
|
|
+ */
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) &&
|
|
+ rrl->hash != NULL) {
|
|
+ rate = rrl->probes;
|
|
+ if (rrl->searches != 0)
|
|
+ rate /= rrl->searches;
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
|
|
+ "increase from %d to %d RRL entries with"
|
|
+ " %d bins; average search length %.1f",
|
|
+ rrl->num_entries, rrl->num_entries+new,
|
|
+ rrl->hash->length, rate);
|
|
+ }
|
|
+
|
|
+ bsize = sizeof(dns_rrl_block_t) + (new-1)*sizeof(dns_rrl_entry_t);
|
|
+ b = isc_mem_get(rrl->mctx, bsize);
|
|
+ if (b == NULL) {
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
|
|
+ "isc_mem_get(%d) failed for RRL entries",
|
|
+ bsize);
|
|
+ return (ISC_R_NOMEMORY);
|
|
+ }
|
|
+ memset(b, 0, bsize);
|
|
+ b->size = bsize;
|
|
+
|
|
+ e = b->entries;
|
|
+ for (i = 0; i < new; ++i, ++e) {
|
|
+ ISC_LINK_INIT(e, hlink);
|
|
+ ISC_LIST_INITANDAPPEND(rrl->lru, e, lru);
|
|
+ }
|
|
+ rrl->num_entries += new;
|
|
+ ISC_LIST_INITANDAPPEND(rrl->blocks, b, link);
|
|
+
|
|
+ return (ISC_R_SUCCESS);
|
|
+}
|
|
+
|
|
+static inline dns_rrl_bin_t *
|
|
+get_bin(dns_rrl_hash_t *hash, unsigned int hval) {
|
|
+ return (&hash->bins[hval % hash->length]);
|
|
+}
|
|
+
|
|
+static void
|
|
+free_old_hash(dns_rrl_t *rrl) {
|
|
+ dns_rrl_hash_t *old_hash;
|
|
+ dns_rrl_bin_t *old_bin;
|
|
+ dns_rrl_entry_t *e, *e_next;
|
|
+
|
|
+ old_hash = rrl->old_hash;
|
|
+ for (old_bin = &old_hash->bins[0];
|
|
+ old_bin < &old_hash->bins[old_hash->length];
|
|
+ ++old_bin)
|
|
+ {
|
|
+ for (e = ISC_LIST_HEAD(*old_bin); e != NULL; e = e_next) {
|
|
+ e_next = ISC_LIST_NEXT(e, hlink);
|
|
+ ISC_LINK_INIT(e, hlink);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ isc_mem_put(rrl->mctx, old_hash,
|
|
+ sizeof(*old_hash)
|
|
+ + (old_hash->length - 1) * sizeof(old_hash->bins[0]));
|
|
+ rrl->old_hash = NULL;
|
|
+}
|
|
+
|
|
+static isc_result_t
|
|
+expand_rrl_hash(dns_rrl_t *rrl, isc_stdtime_t now) {
|
|
+ dns_rrl_hash_t *hash;
|
|
+ int old_bins, new_bins, hsize;
|
|
+ double rate;
|
|
+
|
|
+ if (rrl->old_hash != NULL)
|
|
+ free_old_hash(rrl);
|
|
+
|
|
+ /*
|
|
+ * Most searches fail and so go to the end of the chain.
|
|
+ * Use a small hash table load factor.
|
|
+ */
|
|
+ old_bins = (rrl->hash == NULL) ? 0 : rrl->hash->length;
|
|
+ new_bins = old_bins/8 + old_bins;
|
|
+ if (new_bins < rrl->num_entries)
|
|
+ new_bins = rrl->num_entries;
|
|
+ new_bins = hash_divisor(new_bins);
|
|
+
|
|
+ hsize = sizeof(dns_rrl_hash_t) + (new_bins-1)*sizeof(hash->bins[0]);
|
|
+ hash = isc_mem_get(rrl->mctx, hsize);
|
|
+ if (hash == NULL) {
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_FAIL,
|
|
+ "isc_mem_get(%d) failed for"
|
|
+ " RRL hash table",
|
|
+ hsize);
|
|
+ return (ISC_R_NOMEMORY);
|
|
+ }
|
|
+ memset(hash, 0, hsize);
|
|
+ hash->length = new_bins;
|
|
+ rrl->hash_gen ^= 1;
|
|
+ hash->gen = rrl->hash_gen;
|
|
+
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP) && old_bins != 0) {
|
|
+ rate = rrl->probes;
|
|
+ if (rrl->searches != 0)
|
|
+ rate /= rrl->searches;
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
|
|
+ "increase from %d to %d RRL bins for"
|
|
+ " %d entries; average search length %.1f",
|
|
+ old_bins, new_bins, rrl->num_entries, rate);
|
|
+ }
|
|
+
|
|
+ rrl->old_hash = rrl->hash;
|
|
+ if (rrl->old_hash != NULL)
|
|
+ rrl->old_hash->check_time = now;
|
|
+ rrl->hash = hash;
|
|
+
|
|
+ return (ISC_R_SUCCESS);
|
|
+}
|
|
+
|
|
+static void
|
|
+ref_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, int probes, isc_stdtime_t now) {
|
|
+ /*
|
|
+ * Make the entry most recently used.
|
|
+ */
|
|
+ if (ISC_LIST_HEAD(rrl->lru) != e) {
|
|
+ if (e == rrl->last_logged)
|
|
+ rrl->last_logged = ISC_LIST_PREV(e, lru);
|
|
+ ISC_LIST_UNLINK(rrl->lru, e, lru);
|
|
+ ISC_LIST_PREPEND(rrl->lru, e, lru);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Expand the hash table if it is time and necessary.
|
|
+ * This will leave the newly referenced entry in a chain in the
|
|
+ * old hash table. It will migrate to the new hash table the next
|
|
+ * time it is used or be cut loose when the old hash table is destroyed.
|
|
+ */
|
|
+ rrl->probes += probes;
|
|
+ ++rrl->searches;
|
|
+ if (rrl->searches > 100 &&
|
|
+ delta_rrl_time(rrl->hash->check_time, now) > 1) {
|
|
+ if (rrl->probes/rrl->searches > 2)
|
|
+ expand_rrl_hash(rrl, now);
|
|
+ rrl->hash->check_time = now;
|
|
+ rrl->probes = 0;
|
|
+ rrl->searches = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline isc_boolean_t
|
|
+key_cmp(const dns_rrl_key_t *a, const dns_rrl_key_t *b) {
|
|
+ if (memcmp(a, b, sizeof(dns_rrl_key_t)) == 0)
|
|
+ return (ISC_TRUE);
|
|
+ return (ISC_FALSE);
|
|
+}
|
|
+
|
|
+static inline isc_uint32_t
|
|
+hash_key(const dns_rrl_key_t *key) {
|
|
+ isc_uint32_t hval;
|
|
+ int i;
|
|
+
|
|
+ hval = key->w[0];
|
|
+ for (i = sizeof(*key) / sizeof(key->w[0]) - 1; i >= 0; --i) {
|
|
+ hval = key->w[i] + (hval<<1);
|
|
+ }
|
|
+ return (hval);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Construct the hash table key.
|
|
+ * Use a hash of the DNS query name to save space in the database.
|
|
+ * Collisions result in legitimate rate limiting responses for one
|
|
+ * query name also limiting responses for other names to the
|
|
+ * same client. This is rare and benign enough given the large
|
|
+ * space costs compared to keeping the entire name in the database
|
|
+ * entry or the time costs of dynamic allocation.
|
|
+ */
|
|
+static void
|
|
+make_key(const dns_rrl_t *rrl, dns_rrl_key_t *key,
|
|
+ const isc_sockaddr_t *client_addr,
|
|
+ dns_rdatatype_t qtype, dns_name_t *qname, dns_rdataclass_t qclass,
|
|
+ dns_rrl_rtype_t rtype)
|
|
+{
|
|
+ dns_name_t base;
|
|
+ dns_offsets_t base_offsets;
|
|
+ int labels, i;
|
|
+
|
|
+ memset(key, 0, sizeof(*key));
|
|
+
|
|
+ key->s.rtype = rtype;
|
|
+ if (rtype == DNS_RRL_RTYPE_QUERY) {
|
|
+ key->s.qtype = qtype;
|
|
+ key->s.qclass = qclass & 0xff;
|
|
+ } else if (rtype == DNS_RRL_RTYPE_REFERRAL ||
|
|
+ rtype == DNS_RRL_RTYPE_NODATA) {
|
|
+ /*
|
|
+ * Because there is no qtype in the empty answer sections of
|
|
+ * referral and NODATA responses, count them as the same.
|
|
+ */
|
|
+ key->s.qclass = qclass & 0xff;
|
|
+ }
|
|
+
|
|
+ if (qname != NULL && qname->labels != 0) {
|
|
+ /*
|
|
+ * Ignore the first label of wildcards.
|
|
+ */
|
|
+ if ((qname->attributes & DNS_NAMEATTR_WILDCARD) != 0 &&
|
|
+ (labels = dns_name_countlabels(qname)) > 1)
|
|
+ {
|
|
+ dns_name_init(&base, base_offsets);
|
|
+ dns_name_getlabelsequence(qname, 1, labels-1, &base);
|
|
+ key->s.qname_hash = dns_name_hashbylabel(&base,
|
|
+ ISC_FALSE);
|
|
+ } else {
|
|
+ key->s.qname_hash = dns_name_hashbylabel(qname,
|
|
+ ISC_FALSE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ switch (client_addr->type.sa.sa_family) {
|
|
+ case AF_INET:
|
|
+ key->s.ip[0] = (client_addr->type.sin.sin_addr.s_addr &
|
|
+ rrl->ipv4_mask);
|
|
+ break;
|
|
+ case AF_INET6:
|
|
+ key->s.ipv6 = ISC_TRUE;
|
|
+ memcpy(key->s.ip, &client_addr->type.sin6.sin6_addr,
|
|
+ sizeof(key->s.ip));
|
|
+ for (i = 0; i < DNS_RRL_MAX_PREFIX/32; ++i)
|
|
+ key->s.ip[i] &= rrl->ipv6_mask[i];
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline dns_rrl_rate_t *
|
|
+get_rate(dns_rrl_t *rrl, dns_rrl_rtype_t rtype) {
|
|
+ switch (rtype) {
|
|
+ case DNS_RRL_RTYPE_QUERY:
|
|
+ return (&rrl->responses_per_second);
|
|
+ case DNS_RRL_RTYPE_REFERRAL:
|
|
+ return (&rrl->referrals_per_second);
|
|
+ case DNS_RRL_RTYPE_NODATA:
|
|
+ return (&rrl->nodata_per_second);
|
|
+ case DNS_RRL_RTYPE_NXDOMAIN:
|
|
+ return (&rrl->nxdomains_per_second);
|
|
+ case DNS_RRL_RTYPE_ERROR:
|
|
+ return (&rrl->errors_per_second);
|
|
+ case DNS_RRL_RTYPE_ALL:
|
|
+ return (&rrl->all_per_second);
|
|
+ default:
|
|
+ INSIST(0);
|
|
+ }
|
|
+ return (NULL);
|
|
+}
|
|
+
|
|
+static int
|
|
+response_balance(dns_rrl_t *rrl, const dns_rrl_entry_t *e, int age) {
|
|
+ dns_rrl_rate_t *ratep;
|
|
+ int balance, rate;
|
|
+
|
|
+ if (e->key.s.rtype == DNS_RRL_RTYPE_TCP) {
|
|
+ rate = 1;
|
|
+ } else {
|
|
+ ratep = get_rate(rrl, e->key.s.rtype);
|
|
+ rate = ratep->scaled;
|
|
+ }
|
|
+
|
|
+ balance = e->responses + age * rate;
|
|
+ if (balance > rate)
|
|
+ balance = rate;
|
|
+ return (balance);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Search for an entry for a response and optionally create it.
|
|
+ */
|
|
+static dns_rrl_entry_t *
|
|
+get_entry(dns_rrl_t *rrl, const isc_sockaddr_t *client_addr,
|
|
+ dns_rdataclass_t qclass, dns_rdatatype_t qtype, dns_name_t *qname,
|
|
+ dns_rrl_rtype_t rtype, isc_stdtime_t now, isc_boolean_t create,
|
|
+ char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ dns_rrl_key_t key;
|
|
+ isc_uint32_t hval;
|
|
+ dns_rrl_entry_t *e;
|
|
+ dns_rrl_hash_t *hash;
|
|
+ dns_rrl_bin_t *new_bin, *old_bin;
|
|
+ int probes, age;
|
|
+
|
|
+ make_key(rrl, &key, client_addr, qtype, qname, qclass, rtype);
|
|
+ hval = hash_key(&key);
|
|
+
|
|
+ /*
|
|
+ * Look for the entry in the current hash table.
|
|
+ */
|
|
+ new_bin = get_bin(rrl->hash, hval);
|
|
+ probes = 1;
|
|
+ e = ISC_LIST_HEAD(*new_bin);
|
|
+ while (e != NULL) {
|
|
+ if (key_cmp(&e->key, &key)) {
|
|
+ ref_entry(rrl, e, probes, now);
|
|
+ return (e);
|
|
+ }
|
|
+ ++probes;
|
|
+ e = ISC_LIST_NEXT(e, hlink);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Look in the old hash table.
|
|
+ */
|
|
+ if (rrl->old_hash != NULL) {
|
|
+ old_bin = get_bin(rrl->old_hash, hval);
|
|
+ e = ISC_LIST_HEAD(*old_bin);
|
|
+ while (e != NULL) {
|
|
+ if (key_cmp(&e->key, &key)) {
|
|
+ ISC_LIST_UNLINK(*old_bin, e, hlink);
|
|
+ ISC_LIST_PREPEND(*new_bin, e, hlink);
|
|
+ e->hash_gen = rrl->hash_gen;
|
|
+ ref_entry(rrl, e, probes, now);
|
|
+ return (e);
|
|
+ }
|
|
+ e = ISC_LIST_NEXT(e, hlink);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Discard prevous hash table when all of its entries are old.
|
|
+ */
|
|
+ age = delta_rrl_time(rrl->old_hash->check_time, now);
|
|
+ if (age > rrl->window)
|
|
+ free_old_hash(rrl);
|
|
+ }
|
|
+
|
|
+ if (!create)
|
|
+ return (NULL);
|
|
+
|
|
+ /*
|
|
+ * The entry does not exist, so create it by finding a free entry.
|
|
+ * Keep currently penalized and logged entries.
|
|
+ * Try to make more entries if none are idle.
|
|
+ * Steal the oldest entry if we cannot create more.
|
|
+ */
|
|
+ for (e = ISC_LIST_TAIL(rrl->lru);
|
|
+ e != NULL;
|
|
+ e = ISC_LIST_PREV(e, lru))
|
|
+ {
|
|
+ if (!ISC_LINK_LINKED(e, hlink))
|
|
+ break;
|
|
+ age = get_age(rrl, e, now);
|
|
+ if (age <= 1) {
|
|
+ e = NULL;
|
|
+ break;
|
|
+ }
|
|
+ if (!e->logged && response_balance(rrl, e, age) > 0)
|
|
+ break;
|
|
+ }
|
|
+ if (e == NULL) {
|
|
+ expand_entries(rrl, ISC_MIN((rrl->num_entries+1)/2, 1000));
|
|
+ e = ISC_LIST_TAIL(rrl->lru);
|
|
+ }
|
|
+ if (e->logged)
|
|
+ log_end(rrl, e, ISC_TRUE, log_buf, log_buf_len);
|
|
+ if (ISC_LINK_LINKED(e, hlink)) {
|
|
+ if (e->hash_gen == rrl->hash_gen)
|
|
+ hash = rrl->hash;
|
|
+ else
|
|
+ hash = rrl->old_hash;
|
|
+ old_bin = get_bin(hash, hash_key(&e->key));
|
|
+ ISC_LIST_UNLINK(*old_bin, e, hlink);
|
|
+ }
|
|
+ ISC_LIST_PREPEND(*new_bin, e, hlink);
|
|
+ e->hash_gen = rrl->hash_gen;
|
|
+ e->key = key;
|
|
+ e->ts_valid = ISC_FALSE;
|
|
+ ref_entry(rrl, e, probes, now);
|
|
+ return (e);
|
|
+}
|
|
+
|
|
+static void
|
|
+debit_log(const dns_rrl_entry_t *e, int age, const char *action) {
|
|
+ char buf[sizeof("age=12345678")];
|
|
+ const char *age_str;
|
|
+
|
|
+ if (age == DNS_RRL_FOREVER) {
|
|
+ age_str = "";
|
|
+ } else {
|
|
+ snprintf(buf, sizeof(buf), "age=%d", age);
|
|
+ age_str = buf;
|
|
+ }
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG3,
|
|
+ "rrl %08x %6s responses=%-3d %s",
|
|
+ hash_key(&e->key), age_str, e->responses, action);
|
|
+}
|
|
+
|
|
+static inline dns_rrl_result_t
|
|
+debit_rrl_entry(dns_rrl_t *rrl, dns_rrl_entry_t *e, double qps, double scale,
|
|
+ const isc_sockaddr_t *client_addr, isc_stdtime_t now,
|
|
+ char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ int rate, new_rate, slip, new_slip, age, log_secs, min;
|
|
+ dns_rrl_rate_t *ratep;
|
|
+ dns_rrl_entry_t const *credit_e;
|
|
+
|
|
+ /*
|
|
+ * Pick the rate counter.
|
|
+ * Optionally adjust the rate by the estimated query/second rate.
|
|
+ */
|
|
+ ratep = get_rate(rrl, e->key.s.rtype);
|
|
+ rate = ratep->r;
|
|
+ if (rate == 0)
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+
|
|
+ if (scale < 1.0) {
|
|
+ /*
|
|
+ * The limit for clients that have used TCP is not scaled.
|
|
+ */
|
|
+ credit_e = get_entry(rrl, client_addr,
|
|
+ 0, dns_rdatatype_none, NULL,
|
|
+ DNS_RRL_RTYPE_TCP, now, ISC_FALSE,
|
|
+ log_buf, log_buf_len);
|
|
+ if (credit_e != NULL) {
|
|
+ age = get_age(rrl, e, now);
|
|
+ if (age < rrl->window)
|
|
+ scale = 1.0;
|
|
+ }
|
|
+ }
|
|
+ if (scale < 1.0) {
|
|
+ new_rate = (int) (rate * scale);
|
|
+ if (new_rate < 1)
|
|
+ new_rate = 1;
|
|
+ if (ratep->scaled != new_rate) {
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST,
|
|
+ DNS_RRL_LOG_DEBUG1,
|
|
+ "%d qps scaled %s by %.2f"
|
|
+ " from %d to %d",
|
|
+ (int)qps, ratep->str, scale,
|
|
+ rate, new_rate);
|
|
+ rate = new_rate;
|
|
+ ratep->scaled = rate;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ min = -rrl->window * rate;
|
|
+
|
|
+ /*
|
|
+ * Treat time jumps into the recent past as no time.
|
|
+ * Treat entries older than the window as if they were just created
|
|
+ * Credit other entries.
|
|
+ */
|
|
+ age = get_age(rrl, e, now);
|
|
+ if (age > 0) {
|
|
+ /*
|
|
+ * Credit tokens earned during elapsed time.
|
|
+ */
|
|
+ if (age > rrl->window) {
|
|
+ e->responses = rate;
|
|
+ e->slip_cnt = 0;
|
|
+ } else {
|
|
+ e->responses += rate*age;
|
|
+ if (e->responses > rate) {
|
|
+ e->responses = rate;
|
|
+ e->slip_cnt = 0;
|
|
+ }
|
|
+ }
|
|
+ /*
|
|
+ * Find the seconds since last log message without overflowing
|
|
+ * small counter. This counter is reset when an entry is
|
|
+ * created. It is not necessarily reset when some requests
|
|
+ * are answered provided other requests continue to be dropped
|
|
+ * or slipped. This can happen when the request rate is just
|
|
+ * at the limit.
|
|
+ */
|
|
+ if (e->logged) {
|
|
+ log_secs = e->log_secs;
|
|
+ log_secs += age;
|
|
+ if (log_secs > DNS_RRL_MAX_LOG_SECS || log_secs < 0)
|
|
+ log_secs = DNS_RRL_MAX_LOG_SECS;
|
|
+ e->log_secs = log_secs;
|
|
+ }
|
|
+ }
|
|
+ set_age(rrl, e, now);
|
|
+
|
|
+ /*
|
|
+ * Debit the entry for this response.
|
|
+ */
|
|
+ if (--e->responses >= 0) {
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
|
|
+ debit_log(e, age, "");
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+ }
|
|
+
|
|
+ if (e->responses < min)
|
|
+ e->responses = min;
|
|
+
|
|
+ /*
|
|
+ * Drop this response unless it should slip or leak.
|
|
+ */
|
|
+ slip = rrl->slip.r;
|
|
+ if (slip > 2 && scale < 1.0) {
|
|
+ new_slip = (int) (slip * scale);
|
|
+ if (new_slip < 2)
|
|
+ new_slip = 2;
|
|
+ if (rrl->slip.scaled != new_slip) {
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST,
|
|
+ DNS_RRL_LOG_DEBUG1,
|
|
+ "%d qps scaled slip"
|
|
+ " by %.2f from %d to %d",
|
|
+ (int)qps, scale,
|
|
+ slip, new_slip);
|
|
+ slip = new_slip;
|
|
+ rrl->slip.scaled = slip;
|
|
+ }
|
|
+ }
|
|
+ if (slip != 0 && e->key.s.rtype != DNS_RRL_RTYPE_ALL) {
|
|
+ if (e->slip_cnt++ == 0) {
|
|
+ if ((int) e->slip_cnt >= slip)
|
|
+ e->slip_cnt = 0;
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
|
|
+ debit_log(e, age, "slip");
|
|
+ return (DNS_RRL_RESULT_SLIP);
|
|
+ } else if ((int) e->slip_cnt >= slip) {
|
|
+ e->slip_cnt = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG3))
|
|
+ debit_log(e, age, "drop");
|
|
+ return (DNS_RRL_RESULT_DROP);
|
|
+}
|
|
+
|
|
+static inline dns_rrl_qname_buf_t *
|
|
+get_qname(dns_rrl_t *rrl, const dns_rrl_entry_t *e) {
|
|
+ dns_rrl_qname_buf_t *qbuf;
|
|
+
|
|
+ qbuf = rrl->qnames[e->log_qname];
|
|
+ if (qbuf == NULL || qbuf->e != e)
|
|
+ return (NULL);
|
|
+ return (qbuf);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+free_qname(dns_rrl_t *rrl, dns_rrl_entry_t *e) {
|
|
+ dns_rrl_qname_buf_t *qbuf;
|
|
+
|
|
+ qbuf = get_qname(rrl, e);
|
|
+ if (qbuf != NULL) {
|
|
+ qbuf->e = NULL;
|
|
+ ISC_LIST_APPEND(rrl->qname_free, qbuf, link);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+add_log_str(isc_buffer_t *lb, const char *str, unsigned int str_len) {
|
|
+ isc_region_t region;
|
|
+
|
|
+ isc_buffer_availableregion(lb, ®ion);
|
|
+ if (str_len >= region.length) {
|
|
+ if (region.length <= 0)
|
|
+ return;
|
|
+ str_len = region.length;
|
|
+ }
|
|
+ memcpy(region.base, str, str_len);
|
|
+ isc_buffer_add(lb, str_len);
|
|
+}
|
|
+
|
|
+#define ADD_LOG_CSTR(eb, s) add_log_str(eb, s, sizeof(s)-1)
|
|
+
|
|
+/*
|
|
+ * Build strings for the logs
|
|
+ */
|
|
+static void
|
|
+make_log_buf(dns_rrl_t *rrl, dns_rrl_entry_t *e,
|
|
+ const char *str1, const char *str2, isc_boolean_t plural,
|
|
+ dns_name_t *qname, isc_boolean_t save_qname,
|
|
+ dns_rrl_result_t rrl_result, isc_result_t resp_result,
|
|
+ char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ isc_buffer_t lb;
|
|
+ dns_rrl_qname_buf_t *qbuf;
|
|
+ isc_netaddr_t cidr;
|
|
+ char strbuf[ISC_MAX(sizeof("/123"), sizeof(" (12345678)"))];
|
|
+ const char *rstr;
|
|
+ isc_result_t msg_result;
|
|
+
|
|
+ if (log_buf_len <= 1) {
|
|
+ if (log_buf_len == 1)
|
|
+ log_buf[0] = '\0';
|
|
+ return;
|
|
+ }
|
|
+ isc_buffer_init(&lb, log_buf, log_buf_len-1);
|
|
+
|
|
+ if (str1 != NULL)
|
|
+ add_log_str(&lb, str1, strlen(str1));
|
|
+ if (str2 != NULL)
|
|
+ add_log_str(&lb, str2, strlen(str2));
|
|
+
|
|
+ switch (rrl_result) {
|
|
+ case DNS_RRL_RESULT_OK:
|
|
+ break;
|
|
+ case DNS_RRL_RESULT_DROP:
|
|
+ ADD_LOG_CSTR(&lb, "drop ");
|
|
+ break;
|
|
+ case DNS_RRL_RESULT_SLIP:
|
|
+ ADD_LOG_CSTR(&lb, "slip ");
|
|
+ break;
|
|
+ default:
|
|
+ INSIST(0);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch (e->key.s.rtype) {
|
|
+ case DNS_RRL_RTYPE_QUERY:
|
|
+ break;
|
|
+ case DNS_RRL_RTYPE_REFERRAL:
|
|
+ ADD_LOG_CSTR(&lb, "referral ");
|
|
+ break;
|
|
+ case DNS_RRL_RTYPE_NODATA:
|
|
+ ADD_LOG_CSTR(&lb, "NODATA ");
|
|
+ break;
|
|
+ case DNS_RRL_RTYPE_NXDOMAIN:
|
|
+ ADD_LOG_CSTR(&lb, "NXDOMAIN ");
|
|
+ break;
|
|
+ case DNS_RRL_RTYPE_ERROR:
|
|
+ if (resp_result == ISC_R_SUCCESS) {
|
|
+ ADD_LOG_CSTR(&lb, "error ");
|
|
+ } else {
|
|
+ rstr = isc_result_totext(resp_result);
|
|
+ add_log_str(&lb, rstr, strlen(rstr));
|
|
+ ADD_LOG_CSTR(&lb, " error ");
|
|
+ }
|
|
+ break;
|
|
+ case DNS_RRL_RTYPE_ALL:
|
|
+ ADD_LOG_CSTR(&lb, "all ");
|
|
+ break;
|
|
+ default:
|
|
+ INSIST(0);
|
|
+ }
|
|
+
|
|
+ if (plural)
|
|
+ ADD_LOG_CSTR(&lb, "responses to ");
|
|
+ else
|
|
+ ADD_LOG_CSTR(&lb, "response to ");
|
|
+
|
|
+ memset(&cidr, 0, sizeof(cidr));
|
|
+ if (e->key.s.ipv6) {
|
|
+ snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv6_prefixlen);
|
|
+ cidr.family = AF_INET6;
|
|
+ memset(&cidr.type.in6, 0, sizeof(cidr.type.in6));
|
|
+ memcpy(&cidr.type.in6, e->key.s.ip, sizeof(e->key.s.ip));
|
|
+ } else {
|
|
+ snprintf(strbuf, sizeof(strbuf), "/%d", rrl->ipv4_prefixlen);
|
|
+ cidr.family = AF_INET;
|
|
+ cidr.type.in.s_addr = e->key.s.ip[0];
|
|
+ }
|
|
+ msg_result = isc_netaddr_totext(&cidr, &lb);
|
|
+ if (msg_result != ISC_R_SUCCESS)
|
|
+ ADD_LOG_CSTR(&lb, "?");
|
|
+ add_log_str(&lb, strbuf, strlen(strbuf));
|
|
+
|
|
+ if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY ||
|
|
+ e->key.s.rtype == DNS_RRL_RTYPE_REFERRAL ||
|
|
+ e->key.s.rtype == DNS_RRL_RTYPE_NODATA ||
|
|
+ e->key.s.rtype == DNS_RRL_RTYPE_NXDOMAIN) {
|
|
+ qbuf = get_qname(rrl, e);
|
|
+ if (save_qname && qbuf == NULL &&
|
|
+ qname != NULL && dns_name_isabsolute(qname)) {
|
|
+ /*
|
|
+ * Capture the qname for the "stop limiting" message.
|
|
+ */
|
|
+ qbuf = ISC_LIST_TAIL(rrl->qname_free);
|
|
+ if (qbuf != NULL) {
|
|
+ ISC_LIST_UNLINK(rrl->qname_free, qbuf, link);
|
|
+ } else if (rrl->num_qnames < DNS_RRL_QNAMES) {
|
|
+ qbuf = isc_mem_get(rrl->mctx, sizeof(*qbuf));
|
|
+ if (qbuf != NULL) {
|
|
+ memset(qbuf, 0, sizeof(*qbuf));
|
|
+ ISC_LINK_INIT(qbuf, link);
|
|
+ qbuf->index = rrl->num_qnames;
|
|
+ rrl->qnames[rrl->num_qnames++] = qbuf;
|
|
+ } else {
|
|
+ isc_log_write(dns_lctx,
|
|
+ DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST,
|
|
+ DNS_RRL_LOG_FAIL,
|
|
+ "isc_mem_get(%d)"
|
|
+ " failed for RRL qname",
|
|
+ (int)sizeof(*qbuf));
|
|
+ }
|
|
+ }
|
|
+ if (qbuf != NULL) {
|
|
+ e->log_qname = qbuf->index;
|
|
+ qbuf->e = e;
|
|
+ dns_fixedname_init(&qbuf->qname);
|
|
+ dns_name_copy(qname,
|
|
+ dns_fixedname_name(&qbuf->qname),
|
|
+ NULL);
|
|
+ }
|
|
+ }
|
|
+ if (qbuf != NULL)
|
|
+ qname = dns_fixedname_name(&qbuf->qname);
|
|
+ if (qname != NULL) {
|
|
+ ADD_LOG_CSTR(&lb, " for ");
|
|
+ (void)dns_name_totext(qname, ISC_TRUE, &lb);
|
|
+ } else {
|
|
+ ADD_LOG_CSTR(&lb, " for (?)");
|
|
+ }
|
|
+ if (e->key.s.rtype != DNS_RRL_RTYPE_NXDOMAIN) {
|
|
+ ADD_LOG_CSTR(&lb, " ");
|
|
+ (void)dns_rdataclass_totext(e->key.s.qclass, &lb);
|
|
+ if (e->key.s.rtype == DNS_RRL_RTYPE_QUERY) {
|
|
+ ADD_LOG_CSTR(&lb, " ");
|
|
+ (void)dns_rdatatype_totext(e->key.s.qtype, &lb);
|
|
+ }
|
|
+ }
|
|
+ snprintf(strbuf, sizeof(strbuf), " (%08x)",
|
|
+ e->key.s.qname_hash);
|
|
+ add_log_str(&lb, strbuf, strlen(strbuf));
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * We saved room for '\0'.
|
|
+ */
|
|
+ log_buf[isc_buffer_usedlength(&lb)] = '\0';
|
|
+}
|
|
+
|
|
+static void
|
|
+log_end(dns_rrl_t *rrl, dns_rrl_entry_t *e, isc_boolean_t early,
|
|
+ char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ if (e->logged) {
|
|
+ make_log_buf(rrl, e,
|
|
+ early ? "*" : NULL,
|
|
+ rrl->log_only ? "would stop limiting "
|
|
+ : "stop limiting ",
|
|
+ ISC_TRUE, NULL, ISC_FALSE,
|
|
+ DNS_RRL_RESULT_OK, ISC_R_SUCCESS,
|
|
+ log_buf, log_buf_len);
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
|
|
+ "%s", log_buf);
|
|
+ free_qname(rrl, e);
|
|
+ e->logged = ISC_FALSE;
|
|
+ --rrl->num_logged;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Log messages for streams that have stopped being rate limited.
|
|
+ */
|
|
+static void
|
|
+log_stops(dns_rrl_t *rrl, isc_stdtime_t now, int limit,
|
|
+ char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ dns_rrl_entry_t *e;
|
|
+ int age;
|
|
+
|
|
+ for (e = rrl->last_logged; e != NULL; e = ISC_LIST_PREV(e, lru)) {
|
|
+ if (!e->logged)
|
|
+ continue;
|
|
+ if (now != 0) {
|
|
+ age = get_age(rrl, e, now);
|
|
+ if (age < DNS_RRL_STOP_LOG_SECS ||
|
|
+ response_balance(rrl, e, age) < 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ log_end(rrl, e, now == 0, log_buf, log_buf_len);
|
|
+ if (rrl->num_logged <= 0)
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * Too many messages could stall real work.
|
|
+ */
|
|
+ if (--limit < 0) {
|
|
+ rrl->last_logged = ISC_LIST_PREV(e, lru);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ if (e == NULL) {
|
|
+ INSIST(rrl->num_logged == 0);
|
|
+ rrl->log_stops_time = now;
|
|
+ }
|
|
+ rrl->last_logged = e;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Main rate limit interface.
|
|
+ */
|
|
+dns_rrl_result_t
|
|
+dns_rrl(dns_view_t *view,
|
|
+ const isc_sockaddr_t *client_addr, isc_boolean_t is_tcp,
|
|
+ dns_rdataclass_t qclass, dns_rdatatype_t qtype,
|
|
+ dns_name_t *qname, isc_result_t resp_result, isc_stdtime_t now,
|
|
+ isc_boolean_t wouldlog, char *log_buf, unsigned int log_buf_len)
|
|
+{
|
|
+ dns_rrl_t *rrl;
|
|
+ dns_rrl_rtype_t rtype;
|
|
+ dns_rrl_entry_t *e;
|
|
+ isc_netaddr_t netclient;
|
|
+ int secs;
|
|
+ double qps, scale;
|
|
+ int exempt_match;
|
|
+ isc_result_t result;
|
|
+ dns_rrl_result_t rrl_result;
|
|
+
|
|
+ INSIST(log_buf != NULL && log_buf_len > 0);
|
|
+
|
|
+ rrl = view->rrl;
|
|
+ if (rrl->exempt != NULL) {
|
|
+ isc_netaddr_fromsockaddr(&netclient, client_addr);
|
|
+ result = dns_acl_match(&netclient, NULL, rrl->exempt,
|
|
+ &view->aclenv, &exempt_match, NULL);
|
|
+ if (result == ISC_R_SUCCESS && exempt_match > 0)
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+ }
|
|
+
|
|
+ LOCK(&rrl->lock);
|
|
+
|
|
+ /*
|
|
+ * Estimate total query per second rate when scaling by qps.
|
|
+ */
|
|
+ if (rrl->qps_scale == 0) {
|
|
+ qps = 0.0;
|
|
+ scale = 1.0;
|
|
+ } else {
|
|
+ ++rrl->qps_responses;
|
|
+ secs = delta_rrl_time(rrl->qps_time, now);
|
|
+ if (secs <= 0) {
|
|
+ qps = rrl->qps;
|
|
+ } else {
|
|
+ qps = (1.0*rrl->qps_responses) / secs;
|
|
+ if (secs >= rrl->window) {
|
|
+ if (isc_log_wouldlog(dns_lctx,
|
|
+ DNS_RRL_LOG_DEBUG3))
|
|
+ isc_log_write(dns_lctx,
|
|
+ DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST,
|
|
+ DNS_RRL_LOG_DEBUG3,
|
|
+ "%d responses/%d seconds"
|
|
+ " = %d qps",
|
|
+ rrl->qps_responses, secs,
|
|
+ (int)qps);
|
|
+ rrl->qps = qps;
|
|
+ rrl->qps_responses = 0;
|
|
+ rrl->qps_time = now;
|
|
+ } else if (qps < rrl->qps) {
|
|
+ qps = rrl->qps;
|
|
+ }
|
|
+ }
|
|
+ scale = rrl->qps_scale / qps;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Do maintenance once per second.
|
|
+ */
|
|
+ if (rrl->num_logged > 0 && rrl->log_stops_time != now)
|
|
+ log_stops(rrl, now, 8, log_buf, log_buf_len);
|
|
+
|
|
+ /*
|
|
+ * Notice TCP responses when scaling limits by qps.
|
|
+ * Do not try to rate limit TCP responses.
|
|
+ */
|
|
+ if (is_tcp) {
|
|
+ if (scale < 1.0) {
|
|
+ e = get_entry(rrl, client_addr,
|
|
+ 0, dns_rdatatype_none, NULL,
|
|
+ DNS_RRL_RTYPE_TCP, now, ISC_TRUE,
|
|
+ log_buf, log_buf_len);
|
|
+ if (e != NULL) {
|
|
+ e->responses = -(rrl->window+1);
|
|
+ set_age(rrl, e, now);
|
|
+ }
|
|
+ }
|
|
+ UNLOCK(&rrl->lock);
|
|
+ return (ISC_R_SUCCESS);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Find the right kind of entry, creating it if necessary.
|
|
+ * If that is impossible, then nothing more can be done
|
|
+ */
|
|
+ switch (resp_result) {
|
|
+ case ISC_R_SUCCESS:
|
|
+ rtype = DNS_RRL_RTYPE_QUERY;
|
|
+ break;
|
|
+ case DNS_R_DELEGATION:
|
|
+ rtype = DNS_RRL_RTYPE_REFERRAL;
|
|
+ break;
|
|
+ case DNS_R_NXRRSET:
|
|
+ rtype = DNS_RRL_RTYPE_NODATA;
|
|
+ break;
|
|
+ case DNS_R_NXDOMAIN:
|
|
+ rtype = DNS_RRL_RTYPE_NXDOMAIN;
|
|
+ break;
|
|
+ default:
|
|
+ rtype = DNS_RRL_RTYPE_ERROR;
|
|
+ break;
|
|
+ }
|
|
+ e = get_entry(rrl, client_addr, qclass, qtype, qname, rtype,
|
|
+ now, ISC_TRUE, log_buf, log_buf_len);
|
|
+ if (e == NULL) {
|
|
+ UNLOCK(&rrl->lock);
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+ }
|
|
+
|
|
+ if (isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DEBUG1)) {
|
|
+ /*
|
|
+ * Do not worry about speed or releasing the lock.
|
|
+ * This message appears before messages from debit_rrl_entry().
|
|
+ */
|
|
+ make_log_buf(rrl, e, "consider limiting ", NULL, ISC_FALSE,
|
|
+ qname, ISC_FALSE, DNS_RRL_RESULT_OK, resp_result,
|
|
+ log_buf, log_buf_len);
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DEBUG1,
|
|
+ "%s", log_buf);
|
|
+ }
|
|
+
|
|
+ rrl_result = debit_rrl_entry(rrl, e, qps, scale, client_addr, now,
|
|
+ log_buf, log_buf_len);
|
|
+
|
|
+ if (rrl->all_per_second.r != 0) {
|
|
+ /*
|
|
+ * We must debit the all-per-second token bucket if we have
|
|
+ * an all-per-second limit for the IP address.
|
|
+ * The all-per-second limit determines the log message
|
|
+ * when both limits are hit.
|
|
+ * The response limiting must continue if the
|
|
+ * all-per-second limiting lapses.
|
|
+ */
|
|
+ dns_rrl_entry_t *e_all;
|
|
+ dns_rrl_result_t rrl_all_result;
|
|
+
|
|
+ e_all = get_entry(rrl, client_addr,
|
|
+ 0, dns_rdatatype_none, NULL,
|
|
+ DNS_RRL_RTYPE_ALL, now, ISC_TRUE,
|
|
+ log_buf, log_buf_len);
|
|
+ if (e_all == NULL) {
|
|
+ UNLOCK(&rrl->lock);
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+ }
|
|
+ rrl_all_result = debit_rrl_entry(rrl, e_all, qps, scale,
|
|
+ client_addr, now,
|
|
+ log_buf, log_buf_len);
|
|
+ if (rrl_all_result != DNS_RRL_RESULT_OK) {
|
|
+ int level;
|
|
+
|
|
+ e = e_all;
|
|
+ rrl_result = rrl_all_result;
|
|
+ if (rrl_result == DNS_RRL_RESULT_OK)
|
|
+ level = DNS_RRL_LOG_DEBUG2;
|
|
+ else
|
|
+ level = DNS_RRL_LOG_DEBUG1;
|
|
+ if (isc_log_wouldlog(dns_lctx, level)) {
|
|
+ make_log_buf(rrl, e,
|
|
+ "prefer all-per-second limiting ",
|
|
+ NULL, ISC_TRUE, qname, ISC_FALSE,
|
|
+ DNS_RRL_RESULT_OK, resp_result,
|
|
+ log_buf, log_buf_len);
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, level,
|
|
+ "%s", log_buf);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (rrl_result == DNS_RRL_RESULT_OK) {
|
|
+ UNLOCK(&rrl->lock);
|
|
+ return (DNS_RRL_RESULT_OK);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Log occassionally in the rate-limit category.
|
|
+ */
|
|
+ if ((!e->logged || e->log_secs >= DNS_RRL_MAX_LOG_SECS) &&
|
|
+ isc_log_wouldlog(dns_lctx, DNS_RRL_LOG_DROP)) {
|
|
+ make_log_buf(rrl, e, rrl->log_only ? "would " : NULL,
|
|
+ e->logged ? "continue limiting " : "limit ",
|
|
+ ISC_TRUE, qname, ISC_TRUE,
|
|
+ DNS_RRL_RESULT_OK, resp_result,
|
|
+ log_buf, log_buf_len);
|
|
+ if (!e->logged) {
|
|
+ e->logged = ISC_TRUE;
|
|
+ if (++rrl->num_logged <= 1)
|
|
+ rrl->last_logged = e;
|
|
+ }
|
|
+ e->log_secs = 0;
|
|
+
|
|
+ /*
|
|
+ * Avoid holding the lock.
|
|
+ */
|
|
+ if (!wouldlog) {
|
|
+ UNLOCK(&rrl->lock);
|
|
+ e = NULL;
|
|
+ }
|
|
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RRL,
|
|
+ DNS_LOGMODULE_REQUEST, DNS_RRL_LOG_DROP,
|
|
+ "%s", log_buf);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Make a log message for the caller.
|
|
+ */
|
|
+ if (wouldlog)
|
|
+ make_log_buf(rrl, e,
|
|
+ rrl->log_only ? "would rate limit " : "rate limit ",
|
|
+ NULL, ISC_FALSE, qname, ISC_FALSE,
|
|
+ rrl_result, resp_result, log_buf, log_buf_len);
|
|
+
|
|
+ if (e != NULL) {
|
|
+ /*
|
|
+ * Do not save the qname unless we might need it for
|
|
+ * the ending log message.
|
|
+ */
|
|
+ if (!e->logged)
|
|
+ free_qname(rrl, e);
|
|
+ UNLOCK(&rrl->lock);
|
|
+ }
|
|
+
|
|
+ return (rrl_result);
|
|
+}
|
|
+
|
|
+void
|
|
+dns_rrl_view_destroy(dns_view_t *view) {
|
|
+ dns_rrl_t *rrl;
|
|
+ dns_rrl_block_t *b;
|
|
+ dns_rrl_hash_t *h;
|
|
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
|
|
+ int i;
|
|
+
|
|
+ rrl = view->rrl;
|
|
+ if (rrl == NULL)
|
|
+ return;
|
|
+ view->rrl = NULL;
|
|
+
|
|
+ /*
|
|
+ * Assume the caller takes care of locking the view and anything else.
|
|
+ */
|
|
+
|
|
+ if (rrl->num_logged > 0)
|
|
+ log_stops(rrl, 0, ISC_INT32_MAX, log_buf, sizeof(log_buf));
|
|
+
|
|
+ for (i = 0; i < DNS_RRL_QNAMES; ++i) {
|
|
+ if (rrl->qnames[i] == NULL)
|
|
+ break;
|
|
+ isc_mem_put(rrl->mctx, rrl->qnames[i], sizeof(*rrl->qnames[i]));
|
|
+ }
|
|
+
|
|
+ if (rrl->exempt != NULL)
|
|
+ dns_acl_detach(&rrl->exempt);
|
|
+
|
|
+ DESTROYLOCK(&rrl->lock);
|
|
+
|
|
+ while (!ISC_LIST_EMPTY(rrl->blocks)) {
|
|
+ b = ISC_LIST_HEAD(rrl->blocks);
|
|
+ ISC_LIST_UNLINK(rrl->blocks, b, link);
|
|
+ isc_mem_put(rrl->mctx, b, b->size);
|
|
+ }
|
|
+
|
|
+ h = rrl->hash;
|
|
+ if (h != NULL)
|
|
+ isc_mem_put(rrl->mctx, h,
|
|
+ sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
|
|
+
|
|
+ h = rrl->old_hash;
|
|
+ if (h != NULL)
|
|
+ isc_mem_put(rrl->mctx, h,
|
|
+ sizeof(*h) + (h->length - 1) * sizeof(h->bins[0]));
|
|
+
|
|
+ isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
|
|
+}
|
|
+
|
|
+isc_result_t
|
|
+dns_rrl_init(dns_rrl_t **rrlp, dns_view_t *view, int min_entries) {
|
|
+ dns_rrl_t *rrl;
|
|
+ isc_result_t result;
|
|
+
|
|
+ *rrlp = NULL;
|
|
+
|
|
+ rrl = isc_mem_get(view->mctx, sizeof(*rrl));
|
|
+ if (rrl == NULL)
|
|
+ return (ISC_R_NOMEMORY);
|
|
+ memset(rrl, 0, sizeof(*rrl));
|
|
+ isc_mem_attach(view->mctx, &rrl->mctx);
|
|
+ result = isc_mutex_init(&rrl->lock);
|
|
+ if (result != ISC_R_SUCCESS) {
|
|
+ isc_mem_putanddetach(&rrl->mctx, rrl, sizeof(*rrl));
|
|
+ return (result);
|
|
+ }
|
|
+ isc_stdtime_get(&rrl->ts_bases[0]);
|
|
+
|
|
+ view->rrl = rrl;
|
|
+
|
|
+ result = expand_entries(rrl, min_entries);
|
|
+ if (result != ISC_R_SUCCESS) {
|
|
+ dns_rrl_view_destroy(view);
|
|
+ return (result);
|
|
+ }
|
|
+ result = expand_rrl_hash(rrl, 0);
|
|
+ if (result != ISC_R_SUCCESS) {
|
|
+ dns_rrl_view_destroy(view);
|
|
+ return (result);
|
|
+ }
|
|
+
|
|
+ *rrlp = rrl;
|
|
+ return (ISC_R_SUCCESS);
|
|
+}
|
|
diff -r -u lib/dns/view.c-orig lib/dns/view.c
|
|
--- lib/dns/view.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/view.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -49,6 +49,7 @@
|
|
#include <dns/masterdump.h>
|
|
#include <dns/order.h>
|
|
#include <dns/peer.h>
|
|
+#include <dns/rrl.h>
|
|
#include <dns/rbt.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/request.h>
|
|
@@ -184,6 +185,7 @@
|
|
view->answeracl_exclude = NULL;
|
|
view->denyanswernames = NULL;
|
|
view->answernames_exclude = NULL;
|
|
+ view->rrl = NULL;
|
|
view->provideixfr = ISC_TRUE;
|
|
view->maxcachettl = 7 * 24 * 3600;
|
|
view->maxncachettl = 3 * 3600;
|
|
@@ -195,9 +197,7 @@
|
|
view->maxbits = 0;
|
|
view->v4_aaaa = dns_v4_aaaa_ok;
|
|
view->v4_aaaa_acl = NULL;
|
|
- ISC_LIST_INIT(view->rpz_zones);
|
|
- view->rpz_recursive_only = ISC_TRUE;
|
|
- view->rpz_break_dnssec = ISC_FALSE;
|
|
+ view->rpzs = NULL;
|
|
dns_fixedname_init(&view->dlv_fixed);
|
|
view->managed_keys = NULL;
|
|
view->redirect = NULL;
|
|
@@ -334,10 +334,13 @@
|
|
dns_acache_putdb(view->acache, view->cachedb);
|
|
dns_acache_detach(&view->acache);
|
|
}
|
|
- dns_rpz_view_destroy(view);
|
|
+ if (view->rpzs != NULL)
|
|
+ dns_rpz_detach_rpzs(&view->rpzs);
|
|
+ dns_rrl_view_destroy(view);
|
|
#else
|
|
INSIST(view->acache == NULL);
|
|
- INSIST(ISC_LIST_EMPTY(view->rpz_zones));
|
|
+ INSIST(view->rpzs == NULL);
|
|
+ INSIST(view->rrl == NULL);
|
|
#endif
|
|
if (view->requestmgr != NULL)
|
|
dns_requestmgr_detach(&view->requestmgr);
|
|
diff -r -u lib/dns/win32/libdns.def-orig lib/dns/win32/libdns.def
|
|
--- lib/dns/win32/libdns.def-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/win32/libdns.def 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -130,8 +130,8 @@
|
|
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_subtractrdataset
|
|
dns_db_unregister
|
|
dns_dbiterator_current
|
|
@@ -639,17 +639,22 @@
|
|
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_get
|
|
-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_ready
|
|
dns_rpz_str2policy
|
|
dns_rpz_type2str
|
|
-dns_rpz_view_destroy
|
|
dns_rriterator_current
|
|
dns_rriterator_destroy
|
|
dns_rriterator_first
|
|
@@ -657,6 +662,9 @@
|
|
dns_rriterator_next
|
|
dns_rriterator_nextrrset
|
|
dns_rriterator_pause
|
|
+dns_rrl
|
|
+dns_rrl_init
|
|
+dns_rrl_view_destroy
|
|
dns_sdb_putnamedrr
|
|
dns_sdb_putrdata
|
|
dns_sdb_putrr
|
|
@@ -806,7 +814,7 @@
|
|
dns_zone_forcereload
|
|
dns_zone_forwardupdate
|
|
dns_zone_fulldumptostream
|
|
-dns_zone_get_rpz
|
|
+dns_zone_get_rpz_num
|
|
dns_zone_getadded
|
|
dns_zone_getchecknames
|
|
dns_zone_getclass
|
|
@@ -834,6 +842,7 @@
|
|
dns_zone_getqueryonacl
|
|
dns_zone_getraw
|
|
dns_zone_getrequeststats
|
|
+dns_zone_getrpz_num
|
|
dns_zone_getserial
|
|
dns_zone_getserial2
|
|
dns_zone_getserialupdatemethod
|
|
@@ -871,6 +880,7 @@
|
|
dns_zone_refresh
|
|
dns_zone_rekey
|
|
dns_zone_replacedb
|
|
+dns_zone_rpz_attach
|
|
dns_zone_rpz_enable
|
|
dns_zone_setacache
|
|
dns_zone_setadded
|
|
diff -r -u lib/dns/win32/libdns.dsp-orig lib/dns/win32/libdns.dsp
|
|
--- lib/dns/win32/libdns.dsp-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/win32/libdns.dsp 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -346,6 +346,10 @@
|
|
# End Source File
|
|
# Begin Source File
|
|
|
|
+SOURCE=..\include\dns\rrl.h
|
|
+# End Source File
|
|
+# Begin Source File
|
|
+
|
|
SOURCE=..\include\dns\rriterator.h
|
|
# End Source File
|
|
# Begin Source File
|
|
@@ -650,6 +654,10 @@
|
|
# End Source File
|
|
# Begin Source File
|
|
|
|
+SOURCE=..\rrl.c
|
|
+# End Source File
|
|
+# Begin Source File
|
|
+
|
|
SOURCE=..\rriterator.c
|
|
# End Source File
|
|
# Begin Source File
|
|
diff -r -u lib/dns/win32/libdns.mak-orig lib/dns/win32/libdns.mak
|
|
--- lib/dns/win32/libdns.mak-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/win32/libdns.mak 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -184,6 +184,7 @@
|
|
-@erase "$(INTDIR)\result.obj"
|
|
-@erase "$(INTDIR)\rootns.obj"
|
|
-@erase "$(INTDIR)\rpz.obj"
|
|
+ -@erase "$(INTDIR)\rrl.obj"
|
|
-@erase "$(INTDIR)\sdb.obj"
|
|
-@erase "$(INTDIR)\sdlz.obj"
|
|
-@erase "$(INTDIR)\soa.obj"
|
|
@@ -309,6 +310,7 @@
|
|
"$(INTDIR)\result.obj" \
|
|
"$(INTDIR)\rootns.obj" \
|
|
"$(INTDIR)\rpz.obj" \
|
|
+ "$(INTDIR)\rrl.obj" \
|
|
"$(INTDIR)\rriterator.obj" \
|
|
"$(INTDIR)\sdb.obj" \
|
|
"$(INTDIR)\sdlz.obj" \
|
|
@@ -505,6 +507,8 @@
|
|
-@erase "$(INTDIR)\rootns.sbr"
|
|
-@erase "$(INTDIR)\rpz.obj"
|
|
-@erase "$(INTDIR)\rpz.sbr"
|
|
+ -@erase "$(INTDIR)\rrl.obj"
|
|
+ -@erase "$(INTDIR)\rrl.sbr"
|
|
-@erase "$(INTDIR)\rriterator.obj"
|
|
-@erase "$(INTDIR)\rriterator.sbr"
|
|
-@erase "$(INTDIR)\sdb.obj"
|
|
@@ -651,6 +655,7 @@
|
|
"$(INTDIR)\result.sbr" \
|
|
"$(INTDIR)\rootns.sbr" \
|
|
"$(INTDIR)\rpz.sbr" \
|
|
+ "$(INTDIR)\rrl.sbr" \
|
|
"$(INTDIR)\rriterator.sbr" \
|
|
"$(INTDIR)\sdb.sbr" \
|
|
"$(INTDIR)\sdlz.sbr" \
|
|
@@ -748,6 +753,7 @@
|
|
"$(INTDIR)\result.obj" \
|
|
"$(INTDIR)\rootns.obj" \
|
|
"$(INTDIR)\rpz.obj" \
|
|
+ "$(INTDIR)\rrl.obj" \
|
|
"$(INTDIR)\rriterator.obj" \
|
|
"$(INTDIR)\sdb.obj" \
|
|
"$(INTDIR)\sdlz.obj" \
|
|
@@ -1726,6 +1732,24 @@
|
|
|
|
!ENDIF
|
|
|
|
+SOURCE=..\rrl.c
|
|
+
|
|
+!IF "$(CFG)" == "libdns - Win32 Release"
|
|
+
|
|
+
|
|
+"$(INTDIR)\rrl.obj" : $(SOURCE) "$(INTDIR)"
|
|
+ $(CPP) $(CPP_PROJ) $(SOURCE)
|
|
+
|
|
+
|
|
+!ELSEIF "$(CFG)" == "libdns - Win32 Debug"
|
|
+
|
|
+
|
|
+"$(INTDIR)\rrl.obj" "$(INTDIR)\rrl.sbr" : $(SOURCE) "$(INTDIR)"
|
|
+ $(CPP) $(CPP_PROJ) $(SOURCE)
|
|
+
|
|
+
|
|
+!ENDIF
|
|
+
|
|
SOURCE=..\rriterator.c
|
|
|
|
!IF "$(CFG)" == "libdns - Win32 Release"
|
|
diff -r -u lib/dns/zone.c-orig lib/dns/zone.c
|
|
--- lib/dns/zone.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/dns/zone.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -346,9 +346,10 @@
|
|
isc_boolean_t added;
|
|
|
|
/*%
|
|
- * whether this is a response policy zone
|
|
+ * response policy data to be relayed to the database
|
|
*/
|
|
- isc_boolean_t is_rpz;
|
|
+ dns_rpz_zones_t *rpzs;
|
|
+ dns_rpz_num_t rpz_num;
|
|
|
|
/*%
|
|
* Serial number update method.
|
|
@@ -917,7 +918,8 @@
|
|
zone->nodes = 100;
|
|
zone->privatetype = (dns_rdatatype_t)0xffffU;
|
|
zone->added = ISC_FALSE;
|
|
- zone->is_rpz = ISC_FALSE;
|
|
+ zone->rpzs = NULL;
|
|
+ zone->rpz_num = DNS_RPZ_INVALID_NUM;
|
|
ISC_LIST_INIT(zone->forwards);
|
|
zone->raw = NULL;
|
|
zone->secure = NULL;
|
|
@@ -1021,6 +1023,13 @@
|
|
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);
|
|
@@ -1513,7 +1522,9 @@
|
|
* Set the response policy index and information for a zone.
|
|
*/
|
|
isc_result_t
|
|
-dns_zone_rpz_enable(dns_zone_t *zone) {
|
|
+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.
|
|
@@ -1524,14 +1535,26 @@
|
|
strcmp(zone->db_argv[0], "rbt64") != 0)
|
|
return (ISC_R_NOTIMPLEMENTED);
|
|
|
|
- zone->is_rpz = ISC_TRUE;
|
|
+ /*
|
|
+ * 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);
|
|
}
|
|
|
|
-isc_boolean_t
|
|
-dns_zone_get_rpz(dns_zone_t *zone) {
|
|
- return (zone->is_rpz);
|
|
+dns_rpz_num_t
|
|
+dns_zone_get_rpz_num(dns_zone_t *zone) {
|
|
+ return (zone->rpz_num);
|
|
}
|
|
|
|
static isc_result_t
|
|
@@ -1988,10 +2011,9 @@
|
|
unsigned int options;
|
|
|
|
#ifdef BIND9
|
|
- if (zone->is_rpz) {
|
|
- result = dns_db_rpz_enabled(db, NULL);
|
|
- if (result != ISC_R_SUCCESS)
|
|
- return (result);
|
|
+ if (zone->rpz_num != DNS_RPZ_INVALID_NUM) {
|
|
+ REQUIRE(zone->rpzs != NULL);
|
|
+ dns_db_rpz_attach(db, zone->rpzs, zone->rpz_num);
|
|
}
|
|
#endif
|
|
|
|
@@ -4120,6 +4142,11 @@
|
|
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,
|
|
@@ -13047,6 +13074,12 @@
|
|
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) {
|
|
diff -r -u lib/isccfg/namedconf.c-orig lib/isccfg/namedconf.c
|
|
--- lib/isccfg/namedconf.c-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ lib/isccfg/namedconf.c 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -1054,11 +1054,12 @@
|
|
|
|
/*%
|
|
* response-policy {
|
|
- * zone <string> [ policy (given|disabled|passthru|
|
|
+ * zone <string> [ policy (given|disabled|passthru|drop|
|
|
* nxdomain|nodata|cname <domain> ) ]
|
|
* [ recursive-only yes|no ] [ max-policy-ttl number ] ;
|
|
* } [ recursive-only yes|no ] [ max-policy-ttl number ] ;
|
|
- * [ break-dnssec yes|no ] [ min-ns-dots number ] ;
|
|
+ * [ break-dnssec yes|no ] [ min-ns-dots number ]
|
|
+ * [ qname-wait-recurse yes|no ]
|
|
*/
|
|
|
|
static void
|
|
@@ -1083,7 +1084,7 @@
|
|
|
|
/*
|
|
* Parse
|
|
- * given|disabled|passthru|nxdomain|nodata|cname <domain>
|
|
+ * given|disabled|passthru|drop|nxdomain|nodata|cname <domain>
|
|
*/
|
|
static isc_result_t
|
|
cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
|
|
@@ -1214,8 +1215,11 @@
|
|
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",
|
|
+ "given", "disabled", "passthru", "no-op", "drop", "nxdomain", "nodata",
|
|
"cname", NULL
|
|
};
|
|
static cfg_type_t cfg_type_rpz_policy_name = {
|
|
@@ -1261,6 +1265,7 @@
|
|
{ "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 = {
|
|
@@ -1270,6 +1275,40 @@
|
|
};
|
|
|
|
|
|
+/*
|
|
+ * rate-limit
|
|
+ */
|
|
+static cfg_clausedef_t rrl_clauses[] = {
|
|
+ { "responses-per-second", &cfg_type_uint32, 0 },
|
|
+ { "referrals-per-second", &cfg_type_uint32, 0 },
|
|
+ { "nodata-per-second", &cfg_type_uint32, 0 },
|
|
+ { "nxdomains-per-second", &cfg_type_uint32, 0 },
|
|
+ { "errors-per-second", &cfg_type_uint32, 0 },
|
|
+ { "all-per-second", &cfg_type_uint32, 0 },
|
|
+ { "slip", &cfg_type_uint32, 0 },
|
|
+ { "window", &cfg_type_uint32, 0 },
|
|
+ { "log-only", &cfg_type_boolean, 0 },
|
|
+ { "qps-scale", &cfg_type_uint32, 0 },
|
|
+ { "ipv4-prefix-length", &cfg_type_uint32, 0 },
|
|
+ { "ipv6-prefix-length", &cfg_type_uint32, 0 },
|
|
+ { "exempt-clients", &cfg_type_bracketed_aml, 0 },
|
|
+ { "max-table-size", &cfg_type_uint32, 0 },
|
|
+ { "min-table-size", &cfg_type_uint32, 0 },
|
|
+ { NULL, NULL, 0 }
|
|
+};
|
|
+
|
|
+static cfg_clausedef_t *rrl_clausesets[] = {
|
|
+ rrl_clauses,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static cfg_type_t cfg_type_rrl = {
|
|
+ "rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map,
|
|
+ &cfg_rep_map, rrl_clausesets
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
/*%
|
|
* dnssec-lookaside
|
|
*/
|
|
@@ -1423,6 +1462,7 @@
|
|
CFG_CLAUSEFLAG_NOTCONFIGURED },
|
|
#endif
|
|
{ "response-policy", &cfg_type_rpz, 0 },
|
|
+ { "rate-limit", &cfg_type_rrl, 0 },
|
|
{ NULL, NULL, 0 }
|
|
};
|
|
|
|
diff -r -u named.conf-orig named.conf
|
|
--- named.conf-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ named.conf 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,153 @@
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.3;
|
|
+ notify-source 10.53.0.3;
|
|
+ transfer-source 10.53.0.3;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ listen-on { 10.53.0.3; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+
|
|
+ // check that all of the options are parsed without limiting anything
|
|
+ rate-limit {
|
|
+ responses-per-second 200;
|
|
+ referrals-per-second 220;
|
|
+ nodata-per-second 230;
|
|
+ nxdomains-per-second 240;
|
|
+ errors-per-second 250;
|
|
+ all-per-second 700;
|
|
+ ipv4-prefix-length 24;
|
|
+ ipv6-prefix-length 64;
|
|
+ qps-scale 10;
|
|
+ window 1;
|
|
+ max-table-size 1000;
|
|
+ };
|
|
+
|
|
+};
|
|
+
|
|
+zone "." { type hint; file "hints"; };
|
|
+
|
|
+zone "tld3."{ type master; file "tld3.db"; };
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.2;
|
|
+ notify-source 10.53.0.2;
|
|
+ transfer-source 10.53.0.2;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ statistics-file "named.stats";
|
|
+ listen-on { 10.53.0.2; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+
|
|
+ rate-limit {
|
|
+ responses-per-second 2;
|
|
+ all-per-second 50;
|
|
+ slip 3;
|
|
+ exempt-clients { 10.53.0.7; };
|
|
+
|
|
+ // small enough to force a table expansion
|
|
+ min-table-size 75;
|
|
+ };
|
|
+
|
|
+ additional-from-cache no;
|
|
+};
|
|
+
|
|
+key rndc_key {
|
|
+ secret "1234abcd8765";
|
|
+ algorithm hmac-md5;
|
|
+};
|
|
+controls {
|
|
+ inet 10.53.0.2 port 9953 allow { any; } keys { rndc_key; };
|
|
+};
|
|
+
|
|
+/*
|
|
+ * These log settings have no effect unless "-g" is removed from ../../start.pl
|
|
+ */
|
|
+logging {
|
|
+ channel debug {
|
|
+ file "log-debug";
|
|
+ print-category yes; print-severity yes; severity debug 10;
|
|
+ };
|
|
+ channel queries {
|
|
+ file "log-queries";
|
|
+ print-category yes; print-severity yes; severity info;
|
|
+ };
|
|
+ category rate-limit { debug; queries; };
|
|
+ category queries { debug; queries; };
|
|
+};
|
|
+
|
|
+zone "." { type hint; file "hints"; };
|
|
+
|
|
+zone "tld2."{ type master; file "tld2.db"; };
|
|
+/*
|
|
+ * Copyright (C) 2012, 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.
|
|
+ */
|
|
+
|
|
+
|
|
+controls { /* empty */ };
|
|
+
|
|
+options {
|
|
+ query-source address 10.53.0.1;
|
|
+ notify-source 10.53.0.1;
|
|
+ transfer-source 10.53.0.1;
|
|
+ port 5300;
|
|
+ session-keyfile "session.key";
|
|
+ pid-file "named.pid";
|
|
+ listen-on { 10.53.0.1; };
|
|
+ listen-on-v6 { none; };
|
|
+ notify no;
|
|
+};
|
|
+
|
|
+zone "." {type master; file "root.db";};
|
|
diff -r -u root.db-orig root.db
|
|
--- root.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ root.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,31 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+$TTL 120
|
|
+@ SOA ns. hostmaster.ns. ( 1 3600 1200 604800 60 )
|
|
+@ NS ns.
|
|
+ns. A 10.53.0.1
|
|
+. A 10.53.0.1
|
|
+
|
|
+; limit responses from here
|
|
+tld2. NS ns.tld2.
|
|
+ns.tld2. A 10.53.0.2
|
|
+
|
|
+; limit recursion to here
|
|
+tld3. NS ns.tld3.
|
|
+ns.tld3. A 10.53.0.3
|
|
+
|
|
+; generate SERVFAIL
|
|
+tld4. NS ns.tld3.
|
|
diff -r -u setup.sh-orig setup.sh
|
|
--- setup.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ setup.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,21 @@
|
|
+#!/bin/sh
|
|
+#
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+SYSTEMTESTTOP=..
|
|
+. $SYSTEMTESTTOP/conf.sh
|
|
+. ./clean.sh
|
|
+
|
|
diff -r -u tests.sh-orig tests.sh
|
|
--- tests.sh-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ tests.sh 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,258 @@
|
|
+# Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+# test response rate limiting
|
|
+
|
|
+SYSTEMTESTTOP=..
|
|
+. $SYSTEMTESTTOP/conf.sh
|
|
+
|
|
+#set -x
|
|
+
|
|
+ns1=10.53.0.1 # root, defining the others
|
|
+ns2=10.53.0.2 # test server
|
|
+ns3=10.53.0.3 # secondary test server
|
|
+ns7=10.53.0.7 # whitelisted client
|
|
+
|
|
+USAGE="$0: [-x]"
|
|
+while getopts "x" c; do
|
|
+ case $c in
|
|
+ x) set -x;;
|
|
+ *) echo "$USAGE" 1>&2; exit 1;;
|
|
+ esac
|
|
+done
|
|
+shift `expr $OPTIND - 1 || true`
|
|
+if test "$#" -ne 0; then
|
|
+ echo "$USAGE" 1>&2
|
|
+ exit 1
|
|
+fi
|
|
+# really quit on control-C
|
|
+trap 'exit 1' 1 2 15
|
|
+
|
|
+
|
|
+ret=0
|
|
+setret () {
|
|
+ ret=1
|
|
+ echo "$*"
|
|
+}
|
|
+
|
|
+
|
|
+# Wait until soon after the start of a second to make results consistent.
|
|
+# The start of a second credits a rate limit.
|
|
+# This would be far easier in C or by assuming a modern version of perl.
|
|
+sec_start () {
|
|
+ START=`date`
|
|
+ while true; do
|
|
+ NOW=`date`
|
|
+ if test "$START" != "$NOW"; then
|
|
+ return
|
|
+ fi
|
|
+ $PERL -e 'select(undef, undef, undef, 0.05)' || true
|
|
+ done
|
|
+}
|
|
+
|
|
+
|
|
+# turn off ${HOME}/.digrc
|
|
+HOME=/dev/null; export HOME
|
|
+
|
|
+# $1=result name $2=domain name $3=dig options
|
|
+digcmd () {
|
|
+ OFILE=$1; shift
|
|
+ DIG_DOM=$1; shift
|
|
+ ARGS="+nosearch +time=1 +tries=1 +ignore -p 5300 $* $DIG_DOM @$ns2"
|
|
+ #echo I:dig $ARGS 1>&2
|
|
+ START=`date +%y%m%d%H%M.%S`
|
|
+ RESULT=`$DIG $ARGS 2>&1 | tee $OFILE=TEMP \
|
|
+ | sed -n -e '/^;; AUTHORITY/,/^$/d' \
|
|
+ -e '/^;; ADDITIONAL/,/^$/d' \
|
|
+ -e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \
|
|
+ -e 's/;; flags.* tc .*/TC/p' \
|
|
+ -e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \
|
|
+ -e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \
|
|
+ -e 's/;; connection timed out.*/drop/p' \
|
|
+ -e 's/;; communications error to.*/drop/p' \
|
|
+ | tr -d '\n'`
|
|
+ mv "$OFILE=TEMP" "$OFILE=$RESULT"
|
|
+ touch -t $START "$OFILE=$RESULT"
|
|
+}
|
|
+
|
|
+
|
|
+# $1=number of tests $2=target domain $3=dig options
|
|
+QNUM=1
|
|
+burst () {
|
|
+ BURST_LIMIT=$1; shift
|
|
+ BURST_DOM_BASE="$1"; shift
|
|
+ while test "$BURST_LIMIT" -ge 1; do
|
|
+ CNT=`expr "00$QNUM" : '.*\(...\)'`
|
|
+ eval BURST_DOM="$BURST_DOM_BASE"
|
|
+ FILE="dig.out-$BURST_DOM-$CNT"
|
|
+ digcmd $FILE $BURST_DOM $* &
|
|
+ QNUM=`expr $QNUM + 1`
|
|
+ BURST_LIMIT=`expr "$BURST_LIMIT" - 1`
|
|
+ done
|
|
+}
|
|
+
|
|
+
|
|
+# $1=domain $2=IP address $3=# of IP addresses $4=TC $5=drop
|
|
+# $6=NXDOMAIN $7=SERVFAIL or other errors
|
|
+ck_result() {
|
|
+ BAD=
|
|
+ wait
|
|
+ ADDRS=`ls dig.out-$1-*=$2 2>/dev/null | wc -l`
|
|
+ # count simple truncated and truncated NXDOMAIN as TC
|
|
+ TC=`ls dig.out-$1-*=TC dig.out-$1-*=NXDOMAINTC 2>/dev/null | wc -l`
|
|
+ DROP=`ls dig.out-$1-*=drop 2>/dev/null | wc -l`
|
|
+ # count NXDOMAIN and truncated NXDOMAIN as NXDOMAIN
|
|
+ NXDOMAIN=`ls dig.out-$1-*=NXDOMAIN dig.out-$1-*=NXDOMAINTC 2>/dev/null \
|
|
+ | wc -l`
|
|
+ SERVFAIL=`ls dig.out-$1-*=SERVFAIL 2>/dev/null | wc -l`
|
|
+ if test $ADDRS -ne "$3"; then
|
|
+ setret "I:"$ADDRS" instead of $3 '$2' responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $TC -ne "$4"; then
|
|
+ setret "I:"$TC" instead of $4 truncation responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $DROP -ne "$5"; then
|
|
+ setret "I:"$DROP" instead of $5 dropped responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $NXDOMAIN -ne "$6"; then
|
|
+ setret "I:"$NXDOMAIN" instead of $6 NXDOMAIN responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test $SERVFAIL -ne "$7"; then
|
|
+ setret "I:"$SERVFAIL" instead of $7 error responses for $1"
|
|
+ BAD=yes
|
|
+ fi
|
|
+ if test -z "$BAD"; then
|
|
+ rm -f dig.out-$1-*
|
|
+ fi
|
|
+}
|
|
+
|
|
+
|
|
+ckstats () {
|
|
+ LABEL="$1"; shift
|
|
+ TYPE="$1"; shift
|
|
+ EXPECTED="$1"; shift
|
|
+ C=`sed -n -e "s/[ ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p" \
|
|
+ ns2/named.stats | tail -1`
|
|
+ C=`expr 0$C + 0`
|
|
+ if test "$C" -ne $EXPECTED; then
|
|
+ setret "I:wrong $LABEL $TYPE statistics of $C instead of $EXPECTED"
|
|
+ fi
|
|
+}
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+# Tests of referrals to "." must be done before the hints are loaded
|
|
+# or with "additional-from-cache no"
|
|
+burst 5 a1.tld3 +norec
|
|
+# basic rate limiting
|
|
+burst 3 a1.tld2
|
|
+# 1 second delay allows an additional response.
|
|
+sleep 1
|
|
+burst 10 a1.tld2
|
|
+# Request 30 different qnames to try a wildcard.
|
|
+burst 30 'x$CNT.a2.tld2'
|
|
+# These should be counted and limited but are not. See RT33138.
|
|
+burst 10 'y.x$CNT.a2.tld2'
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# referrals to "."
|
|
+ck_result a1.tld3 '' 2 1 2 0 0
|
|
+# check 13 results including 1 second delay that allows an additional response
|
|
+ck_result a1.tld2 192.0.2.1 3 4 6 0 0
|
|
+
|
|
+# Check the wild card answers.
|
|
+# The parent name of the 30 requests is counted.
|
|
+ck_result 'x*.a2.tld2' 192.0.2.2 2 10 18 0 0
|
|
+
|
|
+# These should be limited but are not. See RT33138.
|
|
+ck_result 'y.x*.a2.tld2' 192.0.2.2 10 0 0 0 0
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+burst 10 'x.a3.tld3'
|
|
+burst 10 'y$CNT.a3.tld3'
|
|
+burst 10 'z$CNT.a4.tld2'
|
|
+
|
|
+# 10 identical recursive responses are limited
|
|
+ck_result 'x.a3.tld3' 192.0.3.3 2 3 5 0 0
|
|
+
|
|
+# 10 different recursive responses are not limited
|
|
+ck_result 'y*.a3.tld3' 192.0.3.3 10 0 0 0 0
|
|
+
|
|
+# 10 different NXDOMAIN responses are limited based on the parent name.
|
|
+# We count 13 responses because we count truncated NXDOMAIN responses
|
|
+# as both truncated and NXDOMAIN.
|
|
+ck_result 'z*.a4.tld2' x 0 3 5 5 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats first dropped 36
|
|
+ckstats first truncated 21
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+burst 10 a5.tld2 +tcp
|
|
+burst 10 a6.tld2 -b $ns7
|
|
+burst 10 a7.tld4
|
|
+burst 2 a8.tld2 AAAA
|
|
+burst 2 a8.tld2 TXT
|
|
+burst 2 a8.tld2 SPF
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# TCP responses are not rate limited
|
|
+ck_result a5.tld2 192.0.2.5 10 0 0 0 0
|
|
+
|
|
+# whitelisted client is not rate limited
|
|
+ck_result a6.tld2 192.0.2.6 10 0 0 0 0
|
|
+
|
|
+# Errors such as SERVFAIL are rate limited.
|
|
+ck_result a7.tld4 x 0 0 8 0 2
|
|
+
|
|
+# NODATA responses are counted as the same regardless of qtype.
|
|
+ck_result a8.tld2 '' 2 2 2 0 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats second dropped 46
|
|
+ckstats second truncated 23
|
|
+
|
|
+
|
|
+#########
|
|
+sec_start
|
|
+
|
|
+# IP TC drop NXDOMAIN SERVFAIL
|
|
+# all-per-second
|
|
+# The qnames are all unique but the client IP address is constant.
|
|
+QNUM=101
|
|
+burst 60 'all$CNT.a9.tld2'
|
|
+
|
|
+ck_result 'a*.a9.tld2' 192.0.2.8 50 0 10 0 0
|
|
+
|
|
+$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s $ns2 stats
|
|
+ckstats final dropped 56
|
|
+ckstats final truncated 23
|
|
+
|
|
+
|
|
+echo "I:exit status: $ret"
|
|
+# exit $ret
|
|
+[ $ret -ne 0 ] && echo "I:test failure overridden"
|
|
+exit 0
|
|
diff -r -u tld2.db-orig tld2.db
|
|
--- tld2.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ tld2.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,47 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+; rate limit response from this zone
|
|
+
|
|
+$TTL 120
|
|
+@ SOA tld2. hostmaster.ns.tld2. ( 1 3600 1200 604800 60 )
|
|
+ NS ns
|
|
+ NS .
|
|
+ns A 10.53.0.2
|
|
+
|
|
+; basic rate limiting
|
|
+a1 A 192.0.2.1
|
|
+
|
|
+; wildcards
|
|
+*.a2 A 192.0.2.2
|
|
+
|
|
+; a3 is in tld3
|
|
+
|
|
+; a4 does not exist to give NXDOMAIN
|
|
+
|
|
+; a5 for TCP requests
|
|
+a5 A 192.0.2.5
|
|
+
|
|
+; a6 for whitelisted clients
|
|
+a6 A 192.0.2.6
|
|
+
|
|
+; a7 for SERVFAIL
|
|
+
|
|
+; a8 for NODATA
|
|
+a8 A 192.0.2.8
|
|
+
|
|
+; a9 for all-per-second limit
|
|
+$GENERATE 101-180 all$.a9 A 192.0.2.8
|
|
diff -r -u tld3.db-orig tld3.db
|
|
--- tld3.db-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ tld3.db 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -0,0 +1,25 @@
|
|
+; Copyright (C) 2012, 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.
|
|
+
|
|
+
|
|
+
|
|
+; rate limit response from this zone
|
|
+
|
|
+$TTL 120
|
|
+@ SOA tld3. hostmaster.ns.tld3. ( 1 3600 1200 604800 60 )
|
|
+ NS ns
|
|
+ NS .
|
|
+ns A 10.53.0.3
|
|
+
|
|
+*.a3 A 192.0.3.3
|
|
diff -r -u version-orig version
|
|
--- version-orig 2004-01-01 00:00:00.000000000 +0000
|
|
+++ version 2004-01-01 00:00:00.000000000 +0000
|
|
@@ -7,6 +7,6 @@
|
|
DESCRIPTION="(Extended Support Version)"
|
|
MAJORVER=9
|
|
MINORVER=9
|
|
-PATCHVER=3
|
|
+PATCHVER=3-rpz2+rl.156.01
|
|
RELEASETYPE=-P
|
|
RELEASEVER=1
|