+4713. [func] Added support for the DNS Response Policy Service
+ (DNSRPS) API, which allows named to use an external
+ response policy daemon when built with
+ "configure --enable-dnsrps". Thanks to Vernon
+ Schryver and Farsight Security. [RT #43376]
+
4712. [bug] "dig +domain" and "dig +search" didn't retain the
search domain when retrying with TCP. [RT #45547]
#include <dns/db.h>
#include <dns/dispatch.h>
#include <dns/dlz.h>
+#include <dns/dnsrps.h>
#include <dns/dns64.h>
#include <dns/dyndb.h>
#include <dns/events.h>
return (result);
}
+typedef struct conf_dnsrps_ctx conf_dnsrps_ctx_t;
+struct conf_dnsrps_ctx {
+ isc_result_t result;
+ char *cstr;
+ size_t cstr_size;
+ isc_mem_t *mctx;
+};
+
+/*
+ * Add to the DNSRPS configuration string.
+ */
+static isc_boolean_t
+conf_dnsrps_sadd(conf_dnsrps_ctx_t *ctx, const char *p, ...) {
+ size_t new_len, cur_len, new_cstr_size;
+ char *new_cstr;
+ va_list args;
+
+ if (ctx->cstr == NULL) {
+ ctx->cstr = isc_mem_get(ctx->mctx, 256);
+ if (ctx->cstr == NULL) {
+ ctx->result = ISC_R_NOMEMORY;
+ return (ISC_FALSE);
+ }
+ ctx->cstr[0] = '\0';
+ ctx->cstr_size = 256;
+ }
+
+ cur_len = strlen(ctx->cstr);
+ va_start(args, p);
+ new_len = vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len,
+ p, args) + 1;
+ va_end(args);
+
+ if (cur_len + new_len <= ctx->cstr_size)
+ return (ISC_TRUE);
+
+ new_cstr_size = ((cur_len + new_len)/256 + 1) * 256;
+ new_cstr = isc_mem_get(ctx->mctx, new_cstr_size);
+ if (new_cstr == NULL) {
+ ctx->result = ISC_R_NOMEMORY;
+ return (ISC_FALSE);
+ }
+
+ memmove(new_cstr, ctx->cstr, cur_len);
+ isc_mem_put(ctx->mctx, ctx->cstr, ctx->cstr_size);
+ ctx->cstr_size = new_cstr_size;
+ ctx->cstr = new_cstr;
+
+ /* cannot use args twice after a single va_start()on some systems */
+ va_start(args, p);
+ vsnprintf(ctx->cstr + cur_len, ctx->cstr_size - cur_len, p, args);
+ va_end(args);
+ return (ISC_TRUE);
+}
+
+/*
+ * Get an DNSRPS configuration value using the global and view options
+ * for the default. Return ISC_FALSE upon failure.
+ */
+static isc_boolean_t
+conf_dnsrps_get(const cfg_obj_t **sub_obj,
+ const cfg_obj_t **maps ,const cfg_obj_t *obj,
+ const char *name, conf_dnsrps_ctx_t *ctx)
+{
+ if (ctx != NULL && ctx->result != ISC_R_SUCCESS) {
+ *sub_obj = NULL;
+ return (ISC_FALSE);
+ }
+
+ *sub_obj = cfg_tuple_get(obj, name);
+ if (cfg_obj_isvoid(*sub_obj)) {
+ *sub_obj = NULL;
+ if (maps != NULL &&
+ ISC_R_SUCCESS != named_config_get(maps, name, sub_obj))
+ *sub_obj = NULL;
+ }
+ return (ISC_TRUE);
+}
+
+/*
+ * Handle a DNSRPS boolean configuration value with the global and view
+ * options providing the default.
+ */
+static void
+conf_dnsrps_yes_no(const cfg_obj_t *obj, const char* name,
+ conf_dnsrps_ctx_t *ctx)
+{
+ const cfg_obj_t *sub_obj;
+
+ if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx))
+ return;
+ if (sub_obj == NULL)
+ return;
+ if (ctx == NULL) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "\"%s\" without \"dnsrps-enable yes\"",
+ name);
+ return;
+ }
+
+ conf_dnsrps_sadd(ctx, " %s %s", name,
+ cfg_obj_asboolean(sub_obj) ? "yes" : "no");
+}
+
+static void
+conf_dnsrps_num(const cfg_obj_t *obj, const char *name,
+ conf_dnsrps_ctx_t *ctx)
+{
+ const cfg_obj_t *sub_obj;
+
+ if (!conf_dnsrps_get(&sub_obj, NULL, obj, name, ctx))
+ return;
+ if (sub_obj == NULL)
+ return;
+ if (ctx == NULL) {
+ cfg_obj_log(obj, named_g_lctx, ISC_LOG_ERROR,
+ "\"%s\" without \"dnsrps-enable yes\"",
+ name);
+ return;
+ }
+
+ conf_dnsrps_sadd(ctx, " %s %d", name, cfg_obj_asuint32(sub_obj));
+}
+
+/*
+ * Convert the parsed RPZ configuration statement to a string for
+ * dns_rpz_new_zones().
+ */
+static isc_result_t
+conf_dnsrps(dns_view_t *view, const cfg_obj_t **maps,
+ isc_boolean_t nsip_enabled, isc_boolean_t nsdname_enabled,
+ dns_rpz_zbits_t *nsip_on, dns_rpz_zbits_t *nsdname_on,
+ char **rps_cstr, size_t *rps_cstr_size,
+ const cfg_obj_t *rpz_obj, const cfg_listelt_t *zone_element)
+{
+ conf_dnsrps_ctx_t ctx;
+ const cfg_obj_t *zone_obj, *obj;
+ dns_rpz_num_t rpz_num;
+ isc_boolean_t on;
+ const char *s;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.result = ISC_R_SUCCESS;
+ ctx.mctx = view->mctx;
+
+ for (rpz_num = 0;
+ zone_element != NULL && ctx.result == ISC_R_SUCCESS;
+ ++rpz_num) {
+ zone_obj = cfg_listelt_value(zone_element);
+
+ s = cfg_obj_asstring(cfg_tuple_get(zone_obj, "zone name"));
+ conf_dnsrps_sadd(&ctx, "zone \"%s\"", s);
+
+ obj = cfg_tuple_get(zone_obj, "policy");
+ if (!cfg_obj_isvoid(obj)) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+ conf_dnsrps_sadd(&ctx, " policy %s", s);
+ if (strcasecmp(s, "cname") == 0) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj,
+ "cname"));
+ conf_dnsrps_sadd(&ctx, " %s", s);
+ }
+ }
+
+ conf_dnsrps_yes_no(zone_obj, "recursive-only", &ctx);
+ conf_dnsrps_yes_no(zone_obj, "log", &ctx);
+ conf_dnsrps_num(zone_obj, "max-policy-ttl", &ctx);
+ obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+ if (!cfg_obj_isvoid(obj)) {
+ if (cfg_obj_asboolean(obj))
+ *nsip_on |= DNS_RPZ_ZBIT(rpz_num);
+ else
+ *nsip_on &= ~DNS_RPZ_ZBIT(rpz_num);
+ }
+ on = ((*nsip_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+ if (nsip_enabled != on)
+ conf_dnsrps_sadd(&ctx, on ? " nsip-enable yes " :
+ " nsip-enable no ");
+ obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+ if (!cfg_obj_isvoid(obj)) {
+ if (cfg_obj_asboolean(obj))
+ *nsdname_on |= DNS_RPZ_ZBIT(rpz_num);
+ else
+ *nsdname_on &= ~DNS_RPZ_ZBIT(rpz_num);
+ }
+ on = ((*nsdname_on & DNS_RPZ_ZBIT(rpz_num)) != 0);
+ if (nsdname_enabled != on)
+ conf_dnsrps_sadd(&ctx, on
+ ? " nsdname-enable yes "
+ : " nsdname-enable no ");
+ conf_dnsrps_sadd(&ctx, ";\n");
+ zone_element = cfg_list_next(zone_element);
+ }
+
+ conf_dnsrps_yes_no(rpz_obj, "recursive-only", &ctx);
+ conf_dnsrps_num(rpz_obj, "max-policy-ttl", &ctx);
+ conf_dnsrps_num(rpz_obj, "min-ns-dots", &ctx);
+ conf_dnsrps_yes_no(rpz_obj, "qname-wait-recurse", &ctx);
+ conf_dnsrps_yes_no(rpz_obj, "break-dnssec", &ctx);
+ if (!nsip_enabled)
+ conf_dnsrps_sadd(&ctx, " nsip-enable no ");
+ if (!nsdname_enabled)
+ conf_dnsrps_sadd(&ctx, " nsdname-enable no ");
+
+ /*
+ * Get the general dnsrpzd parameters from the response-policy
+ * statement in the view and the general options.
+ */
+ if (conf_dnsrps_get(&obj, maps, rpz_obj, "dnsrps-options", &ctx) &&
+ obj != NULL)
+ conf_dnsrps_sadd(&ctx, " %s\n", cfg_obj_asstring(obj));
+
+ if (ctx.result == ISC_R_SUCCESS) {
+ *rps_cstr = ctx.cstr;
+ *rps_cstr_size = ctx.cstr_size;
+ } else {
+ if (ctx.cstr != NULL)
+ isc_mem_put(ctx.mctx, ctx.cstr, ctx.cstr_size);
+ *rps_cstr = NULL;
+ *rps_cstr_size = 0;
+ }
+ return (ctx.result);
+}
+
static isc_result_t
configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
const char *str, const char *msg)
"invalid zone name '%s'", str);
return (DNS_R_EMPTYLABEL);
}
- for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) {
- if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
- &zone->origin)) {
- cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
- "duplicate '%s'", str);
- result = DNS_R_DUPLICATE;
- return (result);
+ if (!view->rpzs->p.dnsrps_enabled) {
+ for (rpz_num = 0;
+ rpz_num < view->rpzs->p.num_zones - 1;
+ ++rpz_num)
+ {
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
+ &zone->origin)) {
+ cfg_obj_log(rpz_obj, named_g_lctx,
+ DNS_RPZ_ERROR_LEVEL,
+ "duplicate '%s'", str);
+ result = DNS_R_DUPLICATE;
+ return (result);
+ }
}
}
if (*old_rpz_okp && !dns_name_equal(&old->origin, &zone->origin))
}
static isc_result_t
-configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
- isc_boolean_t *old_rpz_okp)
+configure_rpz(dns_view_t *view, const cfg_obj_t **maps,
+ const cfg_obj_t *rpz_obj, isc_boolean_t *old_rpz_okp)
{
+ isc_boolean_t dnsrps_enabled;
const cfg_listelt_t *zone_element;
+ char *rps_cstr;
+ size_t rps_cstr_size;
const cfg_obj_t *sub_obj;
isc_boolean_t recursive_only_def;
+ isc_boolean_t nsip_enabled, nsdname_enabled;
+ dns_rpz_zbits_t nsip_on, nsdname_on;
dns_ttl_t ttl_def;
isc_uint32_t minupdateint_def;
dns_rpz_zones_t *zones;
if (zone_element == NULL)
return (ISC_R_SUCCESS);
- result = dns_rpz_new_zones(&view->rpzs, view->mctx,
+#ifdef ENABLE_RPZ_NSIP
+ nsip_enabled = ISC_TRUE;
+ nsdname_enabled = ISC_TRUE;
+#else
+ nsip_enabled = ISC_FALSE;
+ nsdname_enabled = ISC_FALSE;
+#endif
+ sub_obj = cfg_tuple_get(rpz_obj, "nsip-enable");
+ if (!cfg_obj_isvoid(sub_obj))
+ nsip_enabled = cfg_obj_asboolean(sub_obj);
+ nsip_on = nsip_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "nsdname-enable");
+ if (!cfg_obj_isvoid(sub_obj))
+ nsdname_enabled = cfg_obj_asboolean(sub_obj);
+ nsdname_on = nsdname_enabled ? DNS_RPZ_ALL_ZBITS : 0;
+
+ /*
+ * "dnsrps-enable yes|no" can be either a global or response-policy
+ * clause.
+ */
+ dnsrps_enabled = ISC_FALSE;
+ rps_cstr = NULL;
+ rps_cstr_size = 0;
+ sub_obj = NULL;
+ (void)named_config_get(maps, "dnsrps-enable", &sub_obj);
+ if (sub_obj != NULL) {
+ dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+ }
+ sub_obj = cfg_tuple_get(rpz_obj, "dnsrps-enable");
+ if (!cfg_obj_isvoid(sub_obj)) {
+ dnsrps_enabled = cfg_obj_asboolean(sub_obj);
+ }
+#ifdef USE_DNSRPS
+ if (dnsrps_enabled && librpz == NULL) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "\"dnsrps-enable yes\" but %s",
+ librpz_lib_open_emsg.c);
+ return (ISC_R_FAILURE);
+ }
+#else
+ if (dnsrps_enabled) {
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "\"dnsrps-enable yes\" but"
+ " with `./configure --enable-dnsrps`");
+ return (ISC_R_FAILURE);
+ }
+#endif
+
+ if (dnsrps_enabled) {
+ /*
+ * Generate the DNS Response Policy Service
+ * configuration string.
+ */
+ result = conf_dnsrps(view, maps,
+ nsip_enabled, nsdname_enabled,
+ &nsip_on, &nsdname_on,
+ &rps_cstr, &rps_cstr_size,
+ rpz_obj, zone_element);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ result = dns_rpz_new_zones(&view->rpzs, rps_cstr,
+ rps_cstr_size, view->mctx,
named_g_taskmgr, named_g_timermgr);
if (result != ISC_R_SUCCESS)
return (result);
zones = view->rpzs;
+ zones->p.nsip_on = nsip_on;
+ zones->p.nsdname_on = nsdname_on;
+
sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
if (!cfg_obj_isvoid(sub_obj) &&
!cfg_obj_asboolean(sub_obj))
* 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, &zones->p, sizeof(zones->p)) != 0)
+ if (*old_rpz_okp &&
+ old != NULL && memcmp(&old->p, &zones->p, sizeof(zones->p)) != 0)
+ {
+ *old_rpz_okp = ISC_FALSE;
+ }
+
+ if (*old_rpz_okp &&
+ (old == NULL ||
+ old->rps_cstr == NULL) != (zones->rps_cstr == NULL))
+ {
*old_rpz_okp = ISC_FALSE;
+ }
+
+ if (*old_rpz_okp &&
+ (zones->rps_cstr != NULL &&
+ strcmp(old->rps_cstr, zones->rps_cstr) != 0))
+ {
+ *old_rpz_okp = ISC_FALSE;
+ }
+
if (*old_rpz_okp) {
dns_rpz_detach_rpzs(&view->rpzs);
dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
} else if (old != NULL && pview != NULL) {
- pview->rpzs->rpz_ver += 1;
+ ++pview->rpzs->rpz_ver;
view->rpzs->rpz_ver = pview->rpzs->rpz_ver;
- cfg_obj_log(rpz_obj, named_g_lctx, ISC_LOG_DEBUG(1),
+ cfg_obj_log(rpz_obj, named_g_lctx, DNS_RPZ_DEBUG_LEVEL1,
"updated RPZ policy: version %d",
view->rpzs->rpz_ver);
}
obj = NULL;
if (view->rdclass == dns_rdataclass_in && need_hints &&
named_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
- CHECK(configure_rpz(view, obj, &old_rpz_ok));
+ CHECK(configure_rpz(view, maps, obj, &old_rpz_ok));
}
obj = NULL;
ISC_FALSE));
}
+ /*
+ * Check that a master or slave zone was found for each
+ * zone named in the response policy statement
+ * unless we are using RPZ service interface.
+ */
+ if (view->rpzs != NULL && !view->rpzs->p.dnsrps_enabled) {
+ dns_rpz_num_t n;
+
+ for (n = 0; n < view->rpzs->p.num_zones; ++n) {
+ if ((view->rpzs->defined & DNS_RPZ_ZBIT(n)) == 0) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(&view->rpzs->zones[n]->origin,
+ namebuf, sizeof(namebuf));
+ isc_log_write(named_g_lctx,
+ NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER,
+ DNS_RPZ_ERROR_LEVEL, "rpz '%s'"
+ " is not a master or slave zone",
+ namebuf);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+ }
+ }
+
/*
* If we're allowing added zones, then load zone configuration
* from the newzone file for zones that were added during previous
}
/*
- * Note whether this is a response policy zone and which one if so.
+ * Note whether this is a response policy zone and which one if so,
+ * unless we are using RPZ service interface. In that case, the
+ * BIND zone database has nothing to do with rpz and so we don't care.
*/
for (rpz_num = 0; ; ++rpz_num) {
- if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones) {
+ if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones ||
+ view->rpzs->p.dnsrps_enabled)
+ {
rpz_num = DNS_RPZ_INVALID_NUM;
break;
}
(void) named_server_loadnta(server);
+#ifdef USE_DNSRPS
+ /*
+ * Start and connect to the DNS Response Policy Service
+ * daemon, dnsrpzd, for each view that uses DNSRPS.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ result = dns_dnsrps_connect(view->rpzs);
+ if (result != ISC_R_SUCCESS) {
+ view = NULL;
+ goto cleanup;
+ }
+ }
+#endif
+
result = ISC_R_SUCCESS;
cleanup:
server->mctx = mctx;
server->task = NULL;
server->zonemgr = NULL;
+
+#ifdef USE_DNSRPS
+ CHECKFATAL(dns_dnsrps_server_create(),
+ "initializing RPZ service interface");
+#endif
+
/* Initialize server data structures. */
server->interfacemgr = NULL;
ISC_LIST_INIT(server->viewlist);
dns_dt_detach(&server->dtenv);
#endif /* HAVE_DNSTAP */
+#ifdef USE_DNSRPS
+ dns_dnsrps_server_destroy();
+#endif
+
named_controls_destroy(&server->controls);
isc_stats_detach(&server->zonestats);
@BIND9_MAKE_INCLUDES@
-SUBDIRS = dlzexternal dyndb pipelined rndc rsabigexponent tkey
+SUBDIRS = dlzexternal dyndb pipelined rndc rpz rsabigexponent tkey
CINCLUDES = ${ISC_INCLUDES} ${DNS_INCLUDES}
fprintf(stderr, "usage: feature-test <arg>\n");
fprintf(stderr, "args:\n");
fprintf(stderr, " --edns-version\n");
+ fprintf(stderr, " --enable-dnsrps\n");
fprintf(stderr, " --enable-filter-aaaa\n");
fprintf(stderr, " --gethostname\n");
fprintf(stderr, " --gssapi\n");
return (1);
}
+ if (strcmp(argv[1], "--enable-dnsrps") == 0) {
+#ifdef USE_DNSRPS
+ return (0);
+#else
+ return (1);
+#endif
+ }
+
if (strcmp(argv[1], "--enable-filter-aaaa") == 0) {
#ifdef ALLOW_FILTER_AAAA
return (0);
--- /dev/null
+alt-dnsrpzd-license.conf
+dnsrps
--- /dev/null
+# Copyright (C) 2011-2013, 2015, 2016 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+VERSION=@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = ${ISC_INCLUDES} ${DNS_INCLUDES}
+
+CDEFINES =
+CWARNINGS =
+
+DNSLIBS =
+ISCLIBS = ../../../../lib/isc/libisc.@A@
+
+DNSDEPLIBS =
+ISCDEPLIBS = ../../../../lib/isc/libisc.@A@
+
+DEPLIBS = ${ISCDEPLIBS}
+
+LIBS = ${ISCLIBS} @LIBS@
+
+TARGETS = dnsrps@EXEEXT@
+
+DNSRPSOBJS = dnsrps.@O@
+
+SRCS = dnsrps.c
+
+@BIND9_MAKE_RULES@
+
+all: dnsrps@EXEEXT@
+
+dnsrps@EXEEXT@: ${DNSRPSOBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${DNSRPSOBJS} ${LIBS}
+
+clean distclean::
+ rm -f ${TARGETS}
+
--- /dev/null
+#! /bin/sh
+#
+# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+set -e
+
+# Say on stdout whether to test DNSRPS
+# and create dnsrps.conf and dnsrps-slave.conf
+# Note that dnsrps.conf and dnsrps-slave.conf are included in named.conf
+# and differ from dnsrpz.conf which is used by dnsrpzd.
+
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+DNSRPS_CMD=../rpz/dnsrps
+
+AS_NS=
+TEST_DNSRPS=
+MCONF=dnsrps.conf
+SCONF=dnsrps-slave.conf
+USAGE="$0: [-xAD] [-M dnsrps.conf] [-S dnsrps-slave.conf]"
+while getopts "xADM:S:" c; do
+ case $c in
+ x) set -x; DEBUG=-x;;
+ A) AS_NS=yes;;
+ D) TEST_DNSRPS=yes;;
+ M) MCONF="$OPTARG";;
+ S) SCONF="$OPTARG";;
+ *) echo "$USAGE" 1>&2; exit 1;;
+ esac
+done
+shift `expr $OPTIND - 1 || true`
+if [ "$#" -ne 0 ]; then
+ echo "$USAGE" 1>&2
+ exit 1
+fi
+
+# erase any existing conf files
+cat /dev/null > $MCONF
+cat /dev/null > $SCONF
+
+add_conf () {
+ echo "$*" >>$MCONF
+ echo "$*" >>$SCONF
+}
+
+if ! $FEATURETEST --enable-dnsrps; then
+ if [ -n "$TEST_DNSRPS" ]; then
+ add_conf "## DNSRPS disabled at compile time"
+ fi
+ add_conf "#skip"
+ exit 0
+fi
+
+if [ -z "$TEST_DNSRPS" ]; then
+ add_conf "## DNSRPS testing is disabled"
+ add_conf '#skip'
+ exit 0
+fi
+
+if [ ! -x $DNSRPS_CMD ]; then
+ add_conf "## make $DNSRPS_CMD to test DNSRPS"
+ add_conf '#skip'
+ exit 0
+fi
+
+if $DNSRPS_CMD -a >/dev/null; then :
+else
+ add_conf "## DNSRPS provider library is not available"
+ add_conf '#skip'
+ exit 0
+fi
+
+CMN=" dnsrps-options { dnsrpzd-conf ../dnsrpzd.conf
+ dnsrpzd-sock ../dnsrpzd.sock
+ dnsrpzd-rpzf ../dnsrpzd.rpzf
+ dnsrpzd-args '-dddd -L stdout'
+ log-level 3"
+
+MASTER="$CMN"
+if [ -n "$AS_NS" ]; then
+ MASTER="$MASTER
+ qname-as-ns yes
+ ip-as-ns yes"
+fi
+
+# write dnsrps setttings for master resolver
+cat <<EOF >>$MCONF
+$MASTER };
+EOF
+
+# write dnsrps setttings for resolvers that should not start dnsrpzd
+cat <<EOF >>$SCONF
+$CMN
+ dnsrpzd '' }; # do not start dnsrpzd
+EOF
+
+
+# DNSRPS is available.
+# The test should fail if the license is bad.
+add_conf "dnsrps-enable yes;"
+
+# Use alt-dnsrpzd-license.conf if it exists
+CUR_L=dnsrpzd-license-cur.conf
+ALT_L=alt-dnsrpzd-license.conf
+# try ../rpz/alt-dnsrpzd-license.conf if alt-dnsrpzd-license.conf does not exist
+[ -s $ALT_L ] || ALT_L=../rpz/alt-dnsrpzd-license.conf
+if [ -s $ALT_L ]; then
+ SRC_L=$ALT_L
+ USE_ALT=
+else
+ SRC_L=../rpz/dnsrpzd-license.conf
+ USE_ALT="## consider installing alt-dnsrpzd-license.conf"
+fi
+cp $SRC_L $CUR_L
+
+# parse $CUR_L for the license zone name, master IP addresses, and optional
+# transfer-source IP addresses
+eval `sed -n -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'\
+ -e 's/.*zone *\([-a-z0-9]*.license.fastrpz.com\).*/NAME=\1/p' \
+ -e 's/.*farsight_fastrpz_license *\([0-9.]*\);.*/IPV4=\1/p' \
+ -e 's/.*farsight_fastrpz_license *\([0-9a-f:]*\);.*/IPV6=\1/p' \
+ -e 's/.*transfer-source *\([0-9.]*\);.*/TS4=-b\1/p' \
+ -e 's/.*transfer-source *\([0-9a-f:]*\);.*/TS6=-b\1/p' \
+ -e 's/.*transfer-source-v6 *\([0-9a-f:]*\);.*/TS6=-b\1/p' \
+ $CUR_L`
+if [ -z "$NAME" ]; then
+ add_conf "## no DNSRPS tests; no license domain name in $SRC_L"
+ add_conf '#fail'
+ exit 0
+fi
+if [ -z "$IPV4" ]; then
+ IPV4=license1.fastrpz.com
+ TS4=
+fi
+if [ -z "$IPV6" ]; then
+ IPV6=license1.fastrpz.com
+ TS6=
+fi
+
+# This TSIG key is common and NOT a secret
+KEY='hmac-sha256:farsight_fastrpz_license:f405d02b4c8af54855fcebc1'
+
+# Try IPv4 and then IPv6 to deal with IPv6 tunnel and connectivity problems
+if `$DIG -4 -t axfr -y$KEY $TS4 $NAME @$IPV4 \
+ | grep -i "^$NAME.*TXT" >/dev/null`; then
+ exit 0
+fi
+if `$DIG -6 -t axfr -y$KEY $TS6 $NAME @$IPV6 \
+ | grep -i "^$NAME.*TXT" >/dev/null`; then
+ exit 0
+fi
+
+add_conf "## DNSRPS lacks a valid license via $SRC_L"
+[ -z "$USE_ALT" ] || add_conf "$USE_ALT"
+add_conf '#fail'
rm -f ns*/*.key ns*/*.private ns2/tld2s.db ns2/bl.tld2.db
rm -f ns3/bl*.db ns*/*switch ns*/empty.db ns*/empty.db.jnl
rm -f ns5/requests ns5/example.db ns5/bl.db ns5/*.perf
-rm -f */named.memstats */named.run */named.stats */session.key
-rm -f */*.jnl */*.core */*.pid
+rm -f */named.memstats */*.run */named.stats */session.key
+rm -f */*.log */*.jnl */*core */*.pid
rm -f */policy2.db
rm -f ns*/named.lock
+rm -f dnsrps*.conf
+rm -f dnsrpzd-license-cur.conf dnsrpzd.rpzf dnsrpzd.sock dnsrpzd.pid
--- /dev/null
+/*
+ * Copyright (C) 2011-2013, 2016 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * -a exit(0) if dnsrps is available or dlopen() msg if not
+ * -p print the path to dnsrpzd configured in dnsrps so that
+ * dnsrpzd can be run by a setup.sh script.
+ * Exit(1) if dnsrps is not available
+ * -n domain print the serial number of a domain to check if a new
+ * version of a policy zone has been transferred to dnsrpzd.
+ * Exit(1) if dnsrps is not available
+ * -w sec.ond wait for seconds, because `sleep 0.1` is not portable
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <isc/boolean.h>
+#include <isc/util.h>
+
+#ifdef USE_DNSRPS
+#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
+#include <dns/librpz.h>
+
+librpz_t *librpz;
+#else
+typedef struct {char c[120];} librpz_emsg_t;
+#endif
+
+
+static isc_boolean_t link_dnsrps(librpz_emsg_t *emsg);
+
+
+#define USAGE "usage: [-ap] [-n domain] [-w sec.onds]\n"
+
+int
+main(int argc, char **argv) {
+#ifdef USE_DNSRPS
+ char cstr[sizeof("zone ")+1024+10];
+ librpz_clist_t *clist;
+ librpz_client_t *client;
+ librpz_rsp_t *rsp;
+ uint32_t serial;
+#endif
+ double seconds;
+ librpz_emsg_t emsg;
+ char *p;
+ int i;
+
+ while ((i = getopt(argc, argv, "apn:w:")) != -1) {
+ switch (i) {
+ case 'a':
+ if (!link_dnsrps(&emsg)) {
+ printf("I:%s\n", emsg.c);
+ return (1);
+ }
+ return (0);
+
+ case 'p':
+ if (!link_dnsrps(&emsg)) {
+ fprintf(stderr, "## %s\n", emsg.c);
+ return (1);
+ }
+#ifdef USE_DNSRPS
+ printf("%s\n", librpz->dnsrpzd_path);
+ return (0);
+#else
+ INSIST(0);
+#endif
+
+ case 'n':
+ if (!link_dnsrps(&emsg)) {
+ fprintf(stderr, "## %s\n", emsg.c);
+ return (1);
+ }
+#ifdef USE_DNSRPS
+ /*
+ * Get the serial number of a policy zone from
+ * a running dnsrpzd daemon.
+ */
+ clist = librpz->clist_create(&emsg, NULL, NULL,
+ NULL, NULL, NULL);
+ if (clist == NULL) {
+ fprintf(stderr, "## %s: %s\n", optarg, emsg.c);
+ return (1);
+ }
+ snprintf(cstr, sizeof(cstr),
+ "zone %s; dnsrpzd \"\";"
+ " dnsrpzd-sock dnsrpzd.sock;"
+ " dnsrpzd-rpzf dnsrpzd.rpzf",
+ optarg);
+ client = librpz->client_create(&emsg, clist,
+ cstr, true);
+ if (client == NULL) {
+ fprintf(stderr, "## %s\n", emsg.c);
+ return (1);
+ }
+
+ rsp = NULL;
+ if (!librpz->rsp_create(&emsg, &rsp, NULL,
+ client, true, false) ||
+ rsp == NULL) {
+ fprintf(stderr, "## %s\n", emsg.c);
+ librpz->client_detach(&client);
+ return (1);
+ }
+
+ if (!librpz->soa_serial(&emsg, &serial, optarg, rsp)) {
+ fprintf(stderr, "## %s\n", emsg.c);
+ librpz->client_detach(&client);
+ return (1);
+ }
+ librpz->rsp_detach(&rsp);
+ librpz->client_detach(&client);
+ printf("%d\n", serial);
+ return (0);
+#else
+ INSIST(0);
+#endif
+
+ case 'w':
+ seconds = strtod(optarg, &p);
+ if (seconds <= 0 || *p != '\0') {
+ fputs(USAGE, stderr);
+ return (1);
+ }
+ usleep((int)(seconds*1000.0*1000.0));
+ return (0);
+
+ default:
+ fputs(USAGE, stderr);
+ return (1);
+ }
+ }
+ fputs(USAGE, stderr);
+ return (1);
+}
+
+
+static isc_boolean_t
+link_dnsrps(librpz_emsg_t *emsg) {
+#ifdef USE_DNSRPS
+ librpz = librpz_lib_open(emsg, NULL, DNSRPS_LIBRPZ_PATH);
+ if (librpz == NULL)
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+#else
+ snprintf(emsg->c, sizeof(emsg->c), "DNSRPS not configured");
+ return (ISC_FALSE);
+#endif
+}
--- /dev/null
+zone isc.license.fastrpz.com {
+ masters port 53 {
+ KEY farsight_fastrpz_license 104.244.14.176;
+ KEY farsight_fastrpz_license 2620:11c:f008::176;
+ };
+};
+
+key farsight_fastrpz_license {
+ algorithm hmac-sha256; secret "f405d02b4c8af54855fcebc1";
+};
--- /dev/null
+# dnsrpzd configuration.
+
+pid-file ../dnsrpzd.pid
+
+include ../dnsrpzd-license-cur.conf
+
+# configure NOTIFY and zone transfers
+port 5301;
+listen-on port 5301 { 10.53.0.3; };
+allow-notify { 10.53.0.0/24; };
+
+zone "bl0" {type master; file "../ns5/bl.db"; };
+zone "bl1" {type master; file "../ns5/bl.db"; };
+zone "bl2" {type master; file "../ns5/bl.db"; };
+zone "bl3" {type master; file "../ns5/bl.db"; };
+zone "bl4" {type master; file "../ns5/bl.db"; };
+zone "bl5" {type master; file "../ns5/bl.db"; };
+zone "bl6" {type master; file "../ns5/bl.db"; };
+zone "bl7" {type master; file "../ns5/bl.db"; };
+zone "bl8" {type master; file "../ns5/bl.db"; };
+zone "bl9" {type master; file "../ns5/bl.db"; };
+zone "bl10" {type master; file "../ns5/bl.db"; };
+zone "bl11" {type master; file "../ns5/bl.db"; };
+zone "bl12" {type master; file "../ns5/bl.db"; };
+zone "bl13" {type master; file "../ns5/bl.db"; };
+zone "bl14" {type master; file "../ns5/bl.db"; };
+zone "bl15" {type master; file "../ns5/bl.db"; };
+zone "bl16" {type master; file "../ns5/bl.db"; };
+zone "bl17" {type master; file "../ns5/bl.db"; };
+zone "bl18" {type master; file "../ns5/bl.db"; };
+zone "bl19" {type master; file "../ns5/bl.db"; };
+
+zone "bl" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-2" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-given" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-passthru" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-no-op" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-disabled" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-nodata" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-nxdomain" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-cname" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-wildcname" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-garden" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-drop" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl-tcp-only" {type slave; masters port 5300 { 10.53.0.3; }; };
+zone "bl.tld2" {type slave; masters port 5300 { 10.53.0.3; }; };
+
+zone "policy1" {type slave; masters port 5300 { 10.53.0.6; }; };
+zone "policy2" {type slave; masters port 5300 { 10.53.0.7; }; };
--- /dev/null
+NAME='isc.license.fastrpz.com'
+KEY='hmac-sha256:farsight_fastrpz_license:f405d02b4c8af54855fcebc1'
+LSERVER=license1.fastrpz.com
zone "tld2s." {type master; file "tld2s.db";};
-zone "bl.tld2." {type master; file "bl.tld2.db"; notify yes; notify-delay 1;};
+zone "bl.tld2." {type master; file "bl.tld2.db";
+ notify yes; notify-delay 0;};
; regression testing for some old crashes
example.com NS example.org.
+
+domain.com cname foobar.com
session-keyfile "session.key";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
- notify no;
+ notify yes;
minimal-responses no;
response-policy {
min-ns-dots 0
qname-wait-recurse yes
min-update-interval 0
+ nsdname-enable yes
+ nsip-enable yes
;
+
+ include "../dnsrps.conf";
+ also-notify { 10.53.0.3 port 5301; };
+ notify-delay 0;
};
+logging { category rpz { default_debug; }; };
+
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
zone "bl.tld2." {type slave; file "bl.tld2.db"; masters {10.53.0.2;};
request-ixfr no; masterfile-format text;};
-zone "crash1.tld2" {type master; file "crash1";};
-zone "crash2.tld3." {type master; file "crash2";};
+zone "crash1.tld2" {type master; file "crash1"; notify no;};
+zone "crash2.tld3." {type master; file "crash2"; notify no;};
listen-on { 10.53.0.5; };
listen-on-v6 { none; };
ixfr-from-differences yes;
- notify-delay 1;
+ notify-delay 0;
notify yes;
minimal-responses no;
# turn rpz on or off
include "rpz-switch";
+
+ include "../dnsrps-slave.conf";
};
key rndc_key {
NS ns1
ns A 10.53.0.5
ns1 A 10.53.0.5
+
+as-ns TXT "rewritten with ip-as-ns and qname-as-ns"
forwarders { 10.53.0.3; };
minimal-responses no;
- response-policy { zone "policy1" min-update-interval 0; };
+ response-policy {
+ zone "policy1" min-update-interval 0;
+ } qname-wait-recurse yes
+ nsip-enable yes
+ nsdname-enable yes;
+
+ include "../dnsrps-slave.conf";
};
+logging { category rpz { default_debug; }; };
+
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
type slave;
masters { 10.53.0.5; };
file "empty.db";
+ also-notify { 10.53.0.3 port 5301; };
+ notify-delay 0;
allow-transfer { any; };
};
listen-on-v6 { none; };
minimal-responses no;
- response-policy { zone "policy2"; }
- qname-wait-recurse no
- min-update-interval 0;
+ response-policy {
+ zone "policy2";
+ } qname-wait-recurse no
+ nsip-enable yes
+ nsdname-enable yes
+ min-update-interval 0;
+
+ include "../dnsrps-slave.conf";
};
+logging { category rpz { default_debug; }; };
+
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
type slave;
masters { 10.53.0.5; };
file "policy2.db";
+ also-notify { 10.53.0.3 port 5301; };
+ notify-delay 0;
allow-transfer { any; };
request-ixfr no; // force axfr on rndc reload
};
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# touch dnsrps-off to not test with DNSRPS
+
set -e
SYSTEMTESTTOP=..
QPERF=`$SHELL qperf.sh`
-$SHELL clean.sh
+USAGE="$0: [-Dx]"
+DEBUG=
+while getopts "Dx" c; do
+ case $c in
+ x) set -x; DEBUG=-x;;
+ D) TEST_DNSRPS="-D";;
+ *) 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
+
+$SHELL clean.sh $DEBUG
+
+# decide whether to test DNSRPS
+# Note that dnsrps.conf and dnsrps-slave.conf are included in named.conf
+# and differ from dnsrpz.conf which is used by dnsrpzd.
+$SHELL ../rpz/ckdnsrps.sh -A $TEST_DNSRPS $DEBUG
+test -z "`grep 'dnsrps-enable yes' dnsrps.conf`" && TEST_DNSRPS=
# set up test policy zones.
# bl is the main test zone
zone "bl10"; zone "bl11"; zone "bl12"; zone "bl13"; zone "bl14";
zone "bl15"; zone "bl16"; zone "bl17"; zone "bl18"; zone "bl19";
} recursive-only no
+ qname-wait-recurse no
+ nsip-enable yes
+ nsdname-enable yes
max-policy-ttl 90
break-dnssec yes
- qname-wait-recurse no
;
EOF
cp ns2/bl.tld2.db.in ns2/bl.tld2.db
cp ns5/empty.db.in ns5/empty.db
cp ns5/empty.db.in ns5/policy2.db
+
+# Run dnsrpzd to get the license and prime the static policy zones
+if test -n "$TEST_DNSRPS"; then
+ DNSRPZD="`../rpz/dnsrps -p`"
+ cd ns3
+ "$DNSRPZ" -D../dnsrpzd.rpzf -S../dnsrpzd.sock -C../dnsrpzd.conf \
+ -w 0 -dddd -L stdout >./dnsrpzd.run 2>&1
+fi
; prefer policy for largest NS name
update add ns.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.1
update add ns.subsub.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.2
+
+; ip-as-qname rewrites all of tld5
+update add ns.tld5.bl. 300 A 12.12.12.12
send
;
; prefer NSIP policy to NSDNAME policy
update add ns.tld2.rpz-nsdname.bl. 300 CNAME 10.0.0.1
+
+; ip-as-ns rewrites all of tld5
+update add 32.5.0.53.10.rpz-ip.bl. 300 A 12.12.12.12
send
+#!/bin/sh
+#
# Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-# $Id$
-
-
# test response policy zones (RPZ)
+# touch dnsrps-off to not test with DNSRPS
+# touch dnsrps-only to not test with classic RPZ
+
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
ns7=$ns.7 # another rewriting resolver
HAVE_CORE=
-SAVE_RESULTS=
+status=0
+
+DEBUG=
+SAVE_RESULTS=
+DNSRPS_TEST_MODE= # "" to test with and then without DNSRPS
+ARGS=
-USAGE="$0: [-x]"
-while getopts "x" c; do
+USAGE="$0: [-xS] [-D {1,2}]"
+while getopts "xSD:" c; do
case $c in
- x) set -x;;
+ x) set -x; DEBUG=-x; ARGS="$ARGS -x";;
+ S) SAVE_RESULTS=-S; ARGS="$ARGS -S";;
+ D) DNSRPS_TEST_MODE="$OPTARG";; # with or without DNSRPZ
*) echo "$USAGE" 1>&2; exit 1;;
esac
done
fi
}
+DNSRPSCMD=./dnsrps
RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
+# Run the tests twice, first without DNSRPS and then with if it is available
+if [ -z "$DNSRPS_TEST_MODE" ]; then
+ if [ -e dnsrps-only ]; then
+ echo "I:'dnsrps-only' found: skipping native RPZ sub-test"
+ else
+ echo "I:running native RPZ sub-test"
+ $SHELL ./$0 -D1 $ARGS || status=1
+ fi
+
+ if [ -e dnsrps-off ]; then
+ echo "I:'dnsrps-off' found: skipping DNSRPS sub-test"
+ else
+ echo "I:attempting to configure servers with DNSRPS..."
+ $PERL $SYSTEMTESTTOP/stop.pl .
+ $SHELL ./setup.sh -D $DEBUG
+ sed -n 's/^## /I:/p' dnsrps.conf
+ if grep '^#fail' dnsrps.conf >/dev/null; then
+ echo "I:exit status: 1"
+ exit 1
+ fi
+ if test -z "`grep '^#skip' dnsrps.conf`"; then
+ echo "I:running DNSRPS sub-test"
+ $PERL $SYSTEMTESTTOP/start.pl --noclean --restart .
+ $SHELL ./$0 $ARGS -D2 || status=1
+ else
+ echo "I:DNSRPS sub-test skipped"
+ fi
+ fi
+
+ echo "I:exit status: $status"
+ exit $status
+fi
+
+if test -x $DNSRPSCMD; then
+ # speed up the many delays for dnsrpzd by waiting only 0.1 seconds
+ WAIT_CMD="$DNSRPSCMD -w 0.1"
+ TEN_SECS=100
+else
+ WAIT_CMD="sleep 1"
+ TEN_SECS=10
+fi
+
digcmd () {
if test "$1" = TCP; then
shift
# Default to +noauth and @$ns3
# Also default to -bX where X is the @value so that OS X will choose
# the right IP source address.
- digcmd_args=`echo "+noadd +time=2 +tries=1 -p 5300 $*" | \
- sed -e "/@/!s/.*/& @$ns3/" \
- -e '/-b/!s/@\([^ ]*\)/@\1 -b\1/' \
+ digcmd_args=`echo "+nocookie +noadd +time=2 +tries=1 -p 5300 $*" | \
+ sed -e "/@/!s/.*/& @$ns3/" \
+ -e '/-b/!s/@\([^ ]*\)/@\1 -b\1/' \
-e '/+n?o?auth/!s/.*/+noauth &/'`
#echo I:dig $digcmd_args 1>&2
$DIG $digcmd_args
GROUP_NM=
TEST_NUM=0
make_dignm () {
+ TEST_NUM=`expr $TEST_NUM : '\([0-9]*\).*'` # trim '+' characters
TEST_NUM=`expr $TEST_NUM + 1`
DIGNM=dig.out$GROUP_NM-$TEST_NUM
while test -f $DIGNM; do
echo "$*"
}
+# set $SN to the SOA serial number of a zone
+# $1=domain $2=DNS server and client IP address
+get_sn() {
+ SOA=`$DIG -p 5300 +short +norecurse soa "$1" "@$2" "-b$2"`
+ SN=`expr "$SOA" : '[^ ]* [^ ]* \([^ ]*\) .*'`
+ test "$SN" != "" && return
+ echo "I:no serial number from \`dig -p 5300 soa $1 @$2\` in \"$SOA\""
+ exit 1
+}
+
+get_sn_fast () {
+ RSN=`$DNSRPSCMD -n "$1"`
+ #echo "dnsrps serial for $1 is $RSN"
+ if test -z "$RSN"; then
+ echo "I:dnsrps failed to get SOA serial number for $1"
+ exit 1
+ fi
+}
+
+# check that dnsrpzd has loaded its zones
+# $1=domain $2=DNS server IP address
+FZONES=`sed -n -e 's/^zone "\(.*\)".*\(10.53.0..\).*/Z=\1;M=\2/p' dnsrpzd.conf`
+dnsrps_loaded() {
+ test "$DNSRPS_TEST_MODE" = 2 || return
+ n=0
+ for V in $FZONES; do
+ eval "$V"
+ get_sn $Z $M
+ while true; do
+ get_sn_fast "$Z"
+ if test "$SN" -eq "0$RSN"; then
+ #echo "$Z @$M serial=$SN"
+ break
+ fi
+ n=`expr $n + 1`
+ if test "$n" -gt $TEN_SECS; then
+ echo "I:dnsrps serial for $Z is $RSN instead of $SN"
+ exit 1
+ fi
+ $WAIT_CMD
+ done
+ done
+}
+
+# check the serial number in an SOA to ensure that a policy zone has
+# been (re)loaded
+# $1=serial number $2=domain $3=DNS server
+ck_soa() {
+ n=0
+ while true; do
+ if test "$DNSRPS_TEST_MODE" = 2; then
+ get_sn_fast "$2"
+ test "$RSN" -eq "$1" && return
+ else
+ get_sn "$2" "$3"
+ test "$SN" -eq "$1" && return
+ fi
+ n=`expr $n + 1`
+ if test "$n" -gt $TEN_SECS; then
+ echo "I:got serial number \"$SN\" instead of \"$1\" from $2 @$3"
+ return
+ fi
+ $WAIT_CMD
+ done
+}
+
# (re)load the reponse policy zones with the rules in the file $TEST_FILE
load_db () {
if test -n "$TEST_FILE"; then
fi
$PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns$1
load_db
+ dnsrps_loaded
}
# $1=server and irrelevant args $2=error message
else
GROUP_NM=
fi
+ dnsrps_loaded
TEST_NUM=0
}
TEST_FILE=
fi
ckalive $ns3 "I:failed; ns3 server crashed and restarted"
+ dnsrps_loaded
GROUP_NM=
}
digcmd $2 >$DIGNM
#ckalive "$2" "I:server crashed by 'dig $2'" || return 1
ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'`
- ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\} *\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p" $DIGNM`
+ ADDR_TTL=`sed -n -e "s/^[-.a-z0-9]\{1,\}[ ]*\([0-9]*\) IN AA* ${ADDR_ESC}\$/\1/p" $DIGNM`
if test -z "$ADDR_TTL"; then
setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM"
return 1
digcmd nonexistent @$ns2 >proto.nxdomain
digcmd txt-only.tld2 @$ns2 >proto.nodata
-
-status=0
-
start_group "QNAME rewrites" test1
nochange . # 1 do not crash or rewrite root
nxdomain a0-1.tld2 # 2
ckstats $ns6 test1 ns6 0
start_group "NXDOMAIN/NODATA action on QNAME trigger" test1
-nxdomain a0-1.tld2 @$ns6 # 1
-nodata a3-1.tld2 @$ns6 # 2
-nodata a3-2.tld2 @$ns6 # 3 nodata at DNAME itself
-nxdomain a4-2.tld2 @$ns6 # 4 rewrite based on CNAME target
-nxdomain a4-2-cname.tld2 @$ns6 # 5
-nodata a4-3-cname.tld2 @$ns6 # 6
+nxdomain a0-1.tld2 @$ns6 # 1
+nodata a3-1.tld2 @$ns6 # 2
+nodata a3-2.tld2 @$ns6 # 3 nodata at DNAME itself
+nxdomain a4-2.tld2 @$ns6 # 4 rewrite based on CNAME target
+nxdomain a4-2-cname.tld2 @$ns6 # 5
+nodata a4-3-cname.tld2 @$ns6 # 6
addr 12.12.12.12 "a4-1.sub1.tld2 @$ns6" # 7 A replacement
addr 12.12.12.12 "a4-1.sub2.tld2 @$ns6" # 8 A replacement with wildcard
-addr 127.4.4.1 "a4-4.tld2 @$ns6" # 9 prefer 1st conflicting QNAME zone
+addr 127.4.4.1 "a4-4.tld2 @$ns6" # 9 prefer 1st conflicting QNAME zone
addr 12.12.12.12 "nxc1.sub1.tld2 @$ns6" # 10 replace NXDOMAIN w/ CNAME
addr 12.12.12.12 "nxc2.sub1.tld2 @$ns6" # 11 replace NXDOMAIN w/ CNAME chain
-addr 127.6.2.1 "a6-2.tld2 @$ns6" # 12
-addr 56.56.56.56 "a3-6.tld2 @$ns6" # 13 wildcard CNAME
+addr 127.6.2.1 "a6-2.tld2 @$ns6" # 12
+addr 56.56.56.56 "a3-6.tld2 @$ns6" # 13 wildcard CNAME
addr 57.57.57.57 "a3-7.sub1.tld2 @$ns6" # 14 wildcard CNAME
addr 127.0.0.16 "a4-5-cname3.tld2 @$ns6" # 15 CNAME chain
addr 127.0.0.17 "a4-6-cname3.tld2 @$ns6" # 16 stop short in CNAME chain
-nxdomain c1.crash2.tld3 @$ns6 # 17 assert in rbtdb.c
-nxdomain a0-1.tld2 +dnssec @$ns6 # 18 simple DO=1 without sigs
+nxdomain c1.crash2.tld3 @$ns6 # 17 assert in rbtdb.c
+nxdomain a0-1.tld2 +dnssec @$ns6 # 18 simple DO=1 without sigs
nxdomain a0-1s-cname.tld2s +dnssec @$ns6 # 19
-drop a3-8.tld2 any @$ns6 # 20 drop
-
+drop a3-8.tld2 any @$ns6 # 20 drop
end_group
-ckstatsrange $ns3 test1 ns3 22 28
+ckstatsrange $ns3 test1 ns3 22 30
ckstats $ns5 test1 ns5 0
ckstats $ns6 test1 ns6 0
addr 127.0.0.17 "a4-4.tld2 -b $ns1" # 17 client-IP address trigger
nxdomain a7-1.tld2 # 18 slave policy zone (RT34450)
cp ns2/blv2.tld2.db.in ns2/bl.tld2.db
-$RNDCCMD 10.53.0.2 reload bl.tld2
-goodsoa="rpz.tld2. hostmaster.ns.tld2. 2 3600 1200 604800 60"
-for i in 0 1 2 3 4 5 6 7 8 9 10
-do
- soa=`$DIG -p 5300 +short soa bl.tld2 @10.53.0.3 -b10.53.0.3`
- test "$soa" = "$goodsoa" && break
- sleep 1
-done
+$RNDCCMD $ns2 reload bl.tld2
+ck_soa 2 bl.tld2 $ns3
nochange a7-1.tld2 # 19 PASSTHRU
-sleep 1 # ensure that a clock tick has occured so that the reload takes effect
+sleep 1 # ensure that a clock tick has occured so that named will do the reload
cp ns2/blv3.tld2.db.in ns2/bl.tld2.db
-goodsoa="rpz.tld2. hostmaster.ns.tld2. 3 3600 1200 604800 60"
-$RNDCCMD 10.53.0.2 reload bl.tld2
-for i in 0 1 2 3 4 5 6 7 8 9 10
-do
- soa=`$DIG -p 5300 +short soa bl.tld2 @10.53.0.3 -b10.53.0.3`
- test "$soa" = "$goodsoa" && break
- sleep 1
-done
+$RNDCCMD $ns2 reload bl.tld2
+ck_soa 3 bl.tld2 $ns3
nxdomain a7-1.tld2 # 20 slave policy zone (RT34450)
end_group
ckstats $ns3 test2 ns3 12
end_group
ckstats $ns3 'radix tree deletions' ns3 0
-if $FEATURETEST --rpz-nsdname; then
- # these tests assume "min-ns-dots 0"
- start_group "NSDNAME rewrites" test3
- nochange a3-1.tld2 # 1
- nochange a3-1.tld2 +dnssec # 2 this once caused problems
- nxdomain a3-1.sub1.tld2 # 3 NXDOMAIN *.sub1.tld2 by NSDNAME
- nxdomain a3-1.subsub.sub1.tld2
- nxdomain a3-1.subsub.sub1.tld2 -tany
- addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2
- nochange a3-2.tld2. # 7 exempt rewrite by name
- nochange a0-1.tld2. # 8 exempt rewrite by address block
- addr 12.12.12.12 a4-1.tld2 # 9 prefer QNAME policy to NSDNAME
- addr 127.0.0.1 a3-1.sub3.tld2 # 10 prefer policy for largest NSDNAME
- addr 127.0.0.2 a3-1.subsub.sub3.tld2
- nxdomain xxx.crash1.tld2 # 12 dns_db_detachnode() crash
- end_group
- ckstats $ns3 test3 ns3 7
+# these tests assume "min-ns-dots 0"
+start_group "NSDNAME rewrites" test3
+nochange a3-1.tld2 # 1
+nochange a3-1.tld2 +dnssec # 2 this once caused problems
+nxdomain a3-1.sub1.tld2 # 3 NXDOMAIN *.sub1.tld2 by NSDNAME
+nxdomain a3-1.subsub.sub1.tld2
+nxdomain a3-1.subsub.sub1.tld2 -tany
+addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2
+nochange a3-2.tld2. # 7 exempt rewrite by name
+nochange a0-1.tld2. # 8 exempt rewrite by address block
+addr 12.12.12.12 a4-1.tld2 # 9 prefer QNAME policy to NSDNAME
+addr 127.0.0.1 a3-1.sub3.tld2 # 10 prefer policy for largest NSDNAME
+addr 127.0.0.2 a3-1.subsub.sub3.tld2
+nxdomain xxx.crash1.tld2 # 12 dns_db_detachnode() crash
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+ addr 12.12.12.12 as-ns.tld5. # 13 qname-as-ns
+fi
+end_group
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+ ckstats $ns3 test3 ns3 8
else
- echo "I:NSDNAME not checked; named configured with --disable-rpz-nsdname"
+ ckstats $ns3 test3 ns3 7
+fi
+
+# these tests assume "min-ns-dots 0"
+start_group "NSIP rewrites" test4
+nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2
+nochange a3-2.tld2. # 2 exempt rewrite by name
+nochange a0-1.tld2. # 3 exempt rewrite by address block
+nochange a3-1.tld4 # 4 different NS IP address
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+ addr 12.12.12.12 as-ns.tld5. # 5 ip-as-ns
fi
+end_group
-if $FEATURETEST --rpz-nsip; then
- # these tests assume "min-ns-dots 0"
- start_group "NSIP rewrites" test4
- nxdomain a3-1.tld2 # 1 NXDOMAIN for all of tld2
- nochange a3-2.tld2. # 2 exempt rewrite by name
- nochange a0-1.tld2. # 3 exempt rewrite by address block
- nochange a3-1.tld4 # 4 different NS IP address
- end_group
-
- start_group "walled garden NSIP rewrites" test4a
- addr 41.41.41.41 a3-1.tld2 # 1 walled garden for all of tld2
- addr 2041::41 'a3-1.tld2 AAAA' # 2 walled garden for all of tld2
- here a3-1.tld2 TXT <<'EOF' # 3 text message for all of tld2
+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
- ckstats $ns3 test4 ns3 4
+end_group
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+ ckstats $ns3 test4 ns3 5
else
- echo "I:NSIP not checked; named configured with --disable-rpz-nsip"
+ ckstats $ns3 test4 ns3 4
fi
# policies in ./test5 overridden by response-policy{} in ns3/named.conf
ckstats $ns3 bugs ns3 8
-
# superficial test for major performance bugs
QPERF=`sh qperf.sh`
if test -n "$QPERF"; then
date "+I:${TS}checking performance $1"
# Dry run to prime everything
comment "before dry run $1"
+ $RNDCCMD $ns5 notrace
$QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >/dev/null
comment "before real test $1"
PFILE="ns5/$2.perf"
- $RNDCCMD $ns5 notrace
$QPERF -c -1 -l30 -d ns5/requests -s $ns5 -p 5300 >$PFILE
comment "after test $1"
X=`sed -n -e 's/.*Returned *\([^ ]*:\) *\([0-9]*\) .*/\1\2/p' $PFILE \
# get qps with rpz
perf 'with RPZ' rpz 'NOERROR:2900 NXDOMAIN:100 '
RPZ=`trim rpz`
-
# turn off rpz and measure qps again
echo "# RPZ off" >ns5/rpz-switch
RNDCCMD_OUT=`$RNDCCMD $ns5 reload`
echo "I:performance not checked; queryperf not available"
fi
+if [ "$DNSRPS_TEST_MODE" = 2 ]; then
+ echo "I:checking that dnsrpzd is automatically restarted"
+ OLD_PID=`cat dnsrpzd.pid`
+ $KILL "$OLD_PID"
+ n=0
+ while true; do
+ NEW_PID=`cat dnsrpzd.pid 2>/dev/null`
+ if test -n "$NEW_PID" -a "0$OLD_PID" -ne "0$NEW_PID"; then
+ #echo "OLD_PID=$OLD_PID NEW_PID=$NEW_PID"
+ break;
+ fi
+ $DIG -p 5300 +short +norecurse a0-1.tld2 @$ns3 >/dev/null
+ n=`expr $n + 1`
+ if test "$n" -gt $TEN_SECS; then
+ setret "I:dnsrpzd did not restart"
+ break
+ fi
+ $WAIT_CMD
+ done
+fi
+
# restart the main test RPZ server to see if that creates a core file
if test -z "$HAVE_CORE"; then
ttl=`awk '/a3-2 tld2 text/ {print $2}' dig.out.any`
if test ${ttl:=0} -eq 0; then setret I:failed; fi
-echo "I:checking rpz updates/transfers with parent nodes added after children"
+
+echo "I:checking rpz updates/transfers with parent nodes added after children" \
+ | tr -d '\n'
# regression test for RT #36272: the success condition
# is the slave server not crashing.
nsd() {
EOF
sleep 2
}
-
for i in 1 2 3 4 5; do
nsd $ns5 add example.com.policy1. '*.example.com.policy1.'
+ echo . | tr -d '\n'
nsd $ns5 delete example.com.policy1. '*.example.com.policy1.'
+ echo . | tr -d '\n'
done
for i in 1 2 3 4 5; do
nsd $ns5 add '*.example.com.policy1.' example.com.policy1.
+ echo . | tr -d '\n'
nsd $ns5 delete '*.example.com.policy1.' example.com.policy1.
+ echo . | tr -d '\n'
done
+echo
+
-echo "I:checking that going from a empty policy zone works"
+echo "I:checking that going from an empty policy zone works"
nsd $ns5 add '*.x.servfail.policy2.' x.servfail.policy2.
sleep 1
$RNDCCMD $ns7 reload policy2
$DIG z.x.servfail -p 5300 @$ns7 > dig.out.ns7
-grep NXDOMAIN dig.out.ns7 > /dev/null || setret I:failed;
-
-echo "I:checking rpz with delegation fails correctly"
-$DIG -p 5300 @$ns3 ns example.com > dig.out.delegation
-grep "status: SERVFAIL" dig.out.delegation > /dev/null || setret "I:failed"
+grep NXDOMAIN dig.out.ns7 > /dev/null || setret I:failed
+
+# dnsrps does not allow NS RRs in policy zones, so this check
+# with dnsrps results in no rewriting.
+if [ "$DNSRPS_TEST_MODE" = 1 ]; then
+ echo "I:checking rpz with delegation fails correctly"
+ $DIG -p 5300 @$ns3 ns example.com > dig.out.delegation
+ grep "status: SERVFAIL" dig.out.delegation > /dev/null || setret "I:failed"
+fi
-echo "I:exit status: $status"
+[ $status -ne 0 ] && pf=fail || pf=pass
+case $DNSRPS_TEST_MODE in
+ 1) echo "I:status (native RPZ sub-test): $status ($pf)";;
+ 2) echo "I:status (DNSRPS sub-test): $status ($pf)";;
+ *) echo "I:invalid test mode";;
+esac
[ $status -eq 0 ] || exit 1
rm -f dig.out.*
rm -f ns*/named.lock
rm -f ns*/named.memstats
-rm -f ns*/named.run
+rm -f ns*/*.run
rm -f ns2/*.local
rm -f ns2/*.queries
rm -f ns2/named.[0-9]*.conf
rm -f ns2/named.conf
rm -f ns3/named.conf
+rm -f ns*/*core *core
+rm -f dnsrps*.conf dnsrpzd*
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; };
response-policy {
zone "clientip1";
zone "clientip2";
- } qname-wait-recurse no;
+ } qname-wait-recurse no
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "clientip1" { type master; file "db.clientip1"; };
# policy configuration to be tested
response-policy {
zone "clientip21";
- } qname-wait-recurse no;
+ } qname-wait-recurse no
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "clientip21" { type master; file "db.clientip21"; };
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
+ session-keyfile "session.key";
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion yes;
querylog yes;
+
+ # let ns3 start dnsrpzd
+ include "../dnsrps-slave.conf";
};
key rndc_key {
zone "log1" log no;
zone "log2" log yes;
zone "log3"; # missing log clause
- } qname-wait-recurse no;
+ } qname-wait-recurse no
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "log1" { type master; file "db.log1"; };
# policy configuration to be tested
response-policy {
zone "wildcard1" policy NXDOMAIN;
- };
+ } qname-wait-recurse yes
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "wildcard1" { type master; file "db.wildcard1"; };
response-policy {
zone "wildcard2a" policy NXDOMAIN;
zone "wildcard2b" policy NXDOMAIN;
- };
+ } qname-wait-recurse yes
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "wildcard2a" { type master; file "db.wildcard2a"; };
# policy configuration to be tested
response-policy {
zone "wildcard3" policy NXDOMAIN;
- };
+ } qname-wait-recurse yes
+ nsdname-enable yes
+ nsip-enable yes;
# policy zones to be tested
zone "wildcard3" { type master; file "db.wildcard3"; };
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; };
recursion yes;
- response-policy { zone "policy"; };
+ response-policy { zone "policy"; }
+ qname-wait-recurse yes
+ nsip-enable yes
+ nsdname-enable yes;
+
+ include "../dnsrps.conf";
};
zone "policy" { type master; file "policy.db"; };
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
- response-policy { zone "policy"; } nsip-wait-recurse no;
+ response-policy { zone "policy"; } nsip-wait-recurse no
+ qname-wait-recurse yes
+ nsip-enable yes
+ nsdname-enable yes;
+
+ include "../dnsrps.conf";
};
zone "policy" { type master; file "policy.db"; };
notify-source 10.53.0.4;
transfer-source 10.53.0.4;
port 5300;
+ session-keyfile "session.key";
pid-file "named.pid";
listen-on { 10.53.0.4; };
listen-on-v6 { none; };
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
-ret=0
-$FEATURETEST --rpz-nsdname || ret=1
-$FEATURETEST --rpz-nsip || ret=1
-
-if [ $ret != 0 ]; then
- echo "I:This test requires NSIP AND NSDNAME support in RPZ." >&2
- exit 1
-fi
-
exec $SHELL ../testcrypto.sh
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# touch dnsrps-off to not test with DNSRPS
+
+set -e
+
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
-perl testgen.pl
+USAGE="$0: [-xD]"
+DEBUG=
+while getopts "xD" c; do
+ case $c in
+ x) set -x; DEBUG=-x;;
+ D) TEST_DNSRPS="-D";;
+ *) 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
+
+$SHELL clean.sh $DEBUG
+
+$PERL testgen.pl
cp -f ns2/named.default.conf ns2/named.conf
cp -f ns3/named1.conf ns3/named.conf
+
+# decide whether to test DNSRPS
+$SHELL ../rpz/ckdnsrps.sh $TEST_DNSRPS $DEBUG
+test -z "`grep 'dnsrps-enable yes' dnsrps.conf`" && TEST_DNSRPS=
+
+CWD=`pwd`
+cat <<EOF >dnsrpzd.conf
+PID-FILE $CWD/dnsrpzd.pid;
+
+include $CWD/dnsrpzd-license-cur.conf
+
+zone "policy" { type master; file "`pwd`/ns3/policy.db"; };
+EOF
+sed -n -e 's/^ *//' -e "/zone.*.*master/s@file \"@&$CWD/ns2/@p" ns2/*.conf \
+ >>dnsrpzd.conf
+
+# Run dnsrpzd to get the license and prime the static policy zones
+if test -n "$TEST_DNSRPS"; then
+ DNSRPZD="`../rpz/dnsrps -p`"
+ "$DNSRPZD" -D./dnsrpzd.rpzf -S./dnsrpzd.sock -C./dnsrpzd.conf \
+ -w 0 -dddd -L stdout >./dnsrpzd.run 2>&1
+fi
#!/usr/bin/env perl
#
-# Copyright (C) 2015, 2016 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
use strict;
use warnings;
EOB
my $no_option = <<'EOB';
- };
+ } nsdname-enable yes nsip-enable yes;
# policy zones to be tested
EOB
my $qname_wait_recurse = <<'EOB';
- } qname-wait-recurse no;
+ } nsdname-enable yes nsip-enable yes qname-wait-recurse no;
# policy zones to be tested
EOB
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# touch dnsrps-off to not test with DNSRPS
+# touch dnsrps-only to not test with classic RPZ
+
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
+
t=0
+DEBUG=
+DNSRPS_TEST_MODE= # "" to test with and then without DNSRPS
+ARGS=
+
+USAGE="$0: [-xS] [-D {1,2}]"
+while getopts "xSD:" c; do
+ case $c in
+ x) set -x; DEBUG=-x; ARGS="$ARGS -x";;
+ D) DNSRPS_TEST_MODE="$OPTARG";; # with or without DNSRPS
+ *) 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
+
+
+DNSRPSCMD=../rpz/dnsrps
+RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
+
+# Run the tests twice, first without DNSRPS and then with if it is available
+if [ -z "$DNSRPS_TEST_MODE" ]; then
+ if [ -e dnsrps-only ]; then
+ echo "I:'dnsrps-only' found: skipping native RPZ sub-test"
+ else
+ echo "I:running native RPZ sub-test"
+ $SHELL ./$0 -D1 $ARGS || status=1
+ fi
+
+ if [ -e dnsrps-off ]; then
+ echo "I:'dnsrps-off' found: skipping DNSRPS sub-test"
+ else
+ echo "I:attempting to configure servers with DNSRPS..."
+ $SHELL ./setup.sh -D $DEBUG
+ sed -n 's/^## /I:/p' dnsrps.conf
+ if grep '^#fail' dnsrps.conf >/dev/null; then
+ echo "I:exit status: 1"
+ exit 1
+ fi
+ if test -z "`grep '^#skip' dnsrps.conf`"; then
+ $RNDCCMD 10.53.0.2 reload
+ $RNDCCMD 10.53.0.3 reload
+ $RNDCCMD 10.53.0.2 flush
+ $RNDCCMD 10.53.0.3 flush
+ echo "I:running DNSRPS sub-test"
+ $SHELL ./$0 -D2 $ARGS || status=1
+ else
+ echo "I:DNSRPS sub-test skipped"
+ fi
+ fi
+
+ echo "I:exit status: $status"
+ exit $status
+fi
+
# $1 = test name (such as 1a, 1b, etc. for which named.$1.conf exists)
run_server() {
TESTNAME=$1
t=`expr $t + 1`
echo "I:testing $NAME doesn't recurse (${t})"
run_query $TESTNAME $LINE || {
- echo "I:test ${t} failed"
- status=1
+ echo "I:test ${t} failed"
+ status=1
}
}
t=`expr $t + 1`
echo "I:testing $NAME recurses (${t})"
run_query $TESTNAME $LINE && {
- echo "I:test ${t} failed"
- status=1
+ echo "I:test ${t} failed"
+ status=1
}
}
+# show whether and why DNSRPS is enabled or disabled
+sed -n 's/^## /I:/p' dnsrps.conf
+
t=`expr $t + 1`
echo "I:testing that l1.l0 exists without RPZ (${t})"
$DIG $DIGOPTS l1.l0 ns @10.53.0.2 -p 5300 > dig.out.${t}
cp ns2/db.6a.00.policy.local ns2/saved.policy.local
cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
sleep 1
t=`expr $t + 1`
echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
echo "I:removing the NSDNAME policy"
cp ns2/db.6c.00.policy.local ns2/db.6a.00.policy.local
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
sleep 1
echo "I:resuming authority server"
if [ "$CYGWIN" ]; then
sleep 1
[ -s dig.out.${t} ] || continue
grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
- echo "I:test ${t} failed"
- status=1
+ echo "I:test ${t} failed"
+ status=1
}
done
echo "I:adding an NSDNAME policy"
cp ns2/db.6b.00.policy.local ns2/db.6a.00.policy.local
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reload 6a.00.policy.local 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
sleep 1
t=`expr $t + 1`
echo "I:running dig to follow CNAME (blocks, so runs in the background) (${t})"
echo "I:removing the policy zone"
cp ns2/named.default.conf ns2/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 reconfig 2>&1 | sed 's/^/I:ns2 /'
+test -f dnsrpzd.pid && kill -USR1 `cat dnsrpzd.pid`
sleep 1
echo "I:resuming authority server"
if [ "$CYGWIN" ]; then
sleep 1
[ -s dig.out.${t} ] || continue
grep "status: NOERROR" dig.out.${t} > /dev/null 2>&1 || {
- echo "I:test ${t} failed"
- status=1
+ echo "I:test ${t} failed"
+ status=1
}
done
if test $ret != 0; then echo "I:failed"; fi
status=`expr $status + $ret`
-echo "I:exit status: $status"
+[ $status -ne 0 ] && pf=fail || pf=pass
+case $DNSRPS_TEST_MODE in
+ 1) echo "I:status (native RPZ sub-test): $status ($pf)";;
+ 2) echo "I:status (DNSRPS sub-test): $status ($pf)";;
+ *) echo "I:invalid test mode";;
+esac
[ $status -eq 0 ] || exit 1
/* Define if you cannot bind() before connect() for TCP sockets. */
#undef BROKEN_TCP_BIND_BEFORE_CONNECT
+/* dnsrps $librpz_name */
+#undef DNSRPS_LIBRPZ_PATH
+
+/* 0=no DNSRPS 1=static link 2=dlopen() */
+#undef DNSRPS_LIB_OPEN
+
/* Define to enable "rrset-order fixed" syntax. */
#undef DNS_RDATASET_FIXED
/* Define to enable American Fuzzy Lop test harness */
#undef ENABLE_AFL
-/* Define to enable rpz-nsdname rules. */
+/* Define to enable rpz nsdname rules. */
#undef ENABLE_RPZ_NSDNAME
-/* Define to enable rpz-nsip rules. */
+/* Define to enable rpz nsip rules. */
#undef ENABLE_RPZ_NSIP
/* Solaris hack to get select_large_fdset. */
/* Define to allow building of objects for dlopen(). */
#undef ISC_DLZ_DLOPEN
+/* have __attribute__s used in librpz.h */
+#undef LIBRPZ_HAVE_ATTR
+
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Define to use large-system tuning. */
#undef TUNE_LARGE
+/* Enable DNS Response Policy Service API */
+#undef USE_DNSRPS
+
/* Defined if you need to use ioctl(FIONBIO) instead a fcntl call to make
non-blocking. */
#undef USE_FIONBIO_IOCTL
docdir
oldincludedir
includedir
+runstatedir
localstatedir
sharedstatedir
sysconfdir
enable_fixed_rrset
enable_rpz_nsip
enable_rpz_nsdname
+enable_dnsrps_dl
+with_dnsrps_libname
+with_dnsrps_dir
+enable_dnsrps
enable_filter_aaaa
enable_dnstap
with_protobuf_c
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
- libdir localedir mandir
+ libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
--enable-atomic enable machine specific atomic operations
[default=autodetect]
--enable-fixed-rrset enable fixed rrset ordering [default=no]
- --disable-rpz-nsip disable rpz-nsip rules [default=enabled]
- --disable-rpz-nsdname disable rpz-nsdname rules [default=enabled]
+ --disable-rpz-nsip disable rpz nsip rules [default=enabled]
+ --disable-rpz-nsdname disable rpz nsdname rules [default=enabled]
+ --enable-dnsrps-dl DNS Response Policy Service delayed link [default=$librpz_dl]
+ --enable-dnsrps enable DNS Response Policy Service API
--enable-filter-aaaa enable filtering of AAAA records [default=no]
--enable-dnstap enable dnstap support (requires fstrm, protobuf-c)
--enable-querytrace enable very verbose query trace logging [default=no]
--with-kame=PATH use Kame IPv6 default path /usr/local/v6
--with-readline=LIBSPEC specify readline library default auto
+ --with-dnsrps-libname DNSRPS provider library name (librpz.so)
+ --with-dnsrps-dir path to DNSRPS provider library
--with-protobuf-c=path Path where protobuf-c is installed, for dnstap
--with-libfstrm=path Path where libfstrm is installed, for dnstap
--with-docbook-xsl=PATH specify path for Docbook-XSL stylesheets
;;
esac
+#
+# Response policy rewriting using DNS Response Policy Service (DNSRPS)
+# interface.
+#
+# DNSRPS can be compiled into BIND everywhere with a reasonably
+# modern C compiler. It is enabled on systems with dlopen() and librpz.so.
+#
+dnsrps_avail=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for librpz __attribute__s" >&5
+$as_echo_n "checking for librpz __attribute__s... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ extern void f(char *p __attribute__((unused)), ...)
+ __attribute__((format(printf,1,2))) __attribute__((__noreturn__));
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ librpz_have_attr=yes
+
+$as_echo "#define LIBRPZ_HAVE_ATTR 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ librpz_have_attr=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
+$as_echo_n "checking for library containing dlopen... " >&6; }
+if ${ac_cv_search_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_dlopen=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_dlopen+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_dlopen+:} false; then :
+
+else
+ ac_cv_search_dlopen=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
+$as_echo "$ac_cv_search_dlopen" >&6; }
+ac_res=$ac_cv_search_dlopen
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+librpz_dl=yes
+for ac_func in dlopen dlclose dlsym
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ librpz_dl=no
+fi
+done
+
+# Check whether --enable-dnsrps-dl was given.
+if test "${enable_dnsrps_dl+set}" = set; then :
+ enableval=$enable_dnsrps_dl; enable_librpz_dl="$enableval"
+else
+ enable_librpz_dl="$librpz_dl"
+fi
+
+
+# Check whether --with-dnsrps-libname was given.
+if test "${with_dnsrps_libname+set}" = set; then :
+ withval=$with_dnsrps_libname; librpz_name="$withval"
+else
+ librpz_name="librpz.so"
+fi
+
+
+# Check whether --with-dnsrps-dir was given.
+if test "${with_dnsrps_dir+set}" = set; then :
+ withval=$with_dnsrps_dir; librpz_path="$withval/$librpz_name"
+else
+ librpz_path="$librpz_name"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DNSRPS_LIBRPZ_PATH "$librpz_path"
+_ACEOF
+
+if test "x$enable_librpz_dl" = "xyes"; then
+ dnsrps_lib_open=2
+else
+ dnsrps_lib_open=1
+ # Add librpz.so to linked libraries if we are not using dlopen()
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing librpz_client_create" >&5
+$as_echo_n "checking for library containing librpz_client_create... " >&6; }
+if ${ac_cv_search_librpz_client_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char librpz_client_create ();
+int
+main ()
+{
+return librpz_client_create ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' rpz; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_librpz_client_create=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_librpz_client_create+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_librpz_client_create+:} false; then :
+
+else
+ ac_cv_search_librpz_client_create=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_librpz_client_create" >&5
+$as_echo "$ac_cv_search_librpz_client_create" >&6; }
+ac_res=$ac_cv_search_librpz_client_create
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+ dnsrps_lib_open=0
+ dnsrps_avail=no
+fi
+
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define DNSRPS_LIB_OPEN $dnsrps_lib_open
+_ACEOF
+
+
+# Check whether --enable-dnsrps was given.
+if test "${enable_dnsrps+set}" = set; then :
+ enableval=$enable_dnsrps; enable_dnsrps=$enableval
+else
+ enable_dnsrps=no
+fi
+
+if test "x$enable_dnsrps" != "xno"; then
+ if test "x$dnsrps_avail" != "xyes"; then
+ as_fn_error $? "dlopen and librpz.so needed for DNSRPS" "$LINENO" 5
+ fi
+ if test "x$dnsrps_lib_open" = "x0"; then
+ as_fn_error $? "dlopen and librpz.so needed for DNSRPS" "$LINENO" 5
+ fi
+
+$as_echo "#define USE_DNSRPS 1" >>confdefs.h
+
+fi
+
#
# Activate "filter-aaaa-on-v4/v6" or not?
#
# elsewhere if there's a good reason for doing so.
#
-ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/ns/Makefile lib/ns/include/Makefile lib/ns/include/ns/Makefile lib/ns/tests/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
+ac_config_files="$ac_config_files make/Makefile make/mkdep Makefile bin/Makefile bin/check/Makefile bin/confgen/Makefile bin/confgen/unix/Makefile bin/delv/Makefile bin/dig/Makefile bin/dnssec/Makefile bin/named/Makefile bin/named/unix/Makefile bin/nsupdate/Makefile bin/pkcs11/Makefile bin/python/Makefile bin/python/isc/Makefile bin/python/isc/utils.py bin/python/isc/tests/Makefile bin/python/dnssec-checkds.py bin/python/dnssec-coverage.py bin/python/dnssec-keymgr.py bin/python/isc/__init__.py bin/python/isc/checkds.py bin/python/isc/coverage.py bin/python/isc/dnskey.py bin/python/isc/eventlist.py bin/python/isc/keydict.py bin/python/isc/keyevent.py bin/python/isc/keymgr.py bin/python/isc/keyseries.py bin/python/isc/keyzone.py bin/python/isc/policy.py bin/python/isc/rndc.py bin/python/isc/tests/dnskey_test.py bin/python/isc/tests/policy_test.py bin/rndc/Makefile bin/tests/Makefile bin/tests/atomic/Makefile bin/tests/db/Makefile bin/tests/dst/Makefile bin/tests/dst/Kdh.+002+18602.key bin/tests/dst/Kdh.+002+18602.private bin/tests/dst/Kdh.+002+48957.key bin/tests/dst/Kdh.+002+48957.private bin/tests/dst/Ktest.+001+00002.key bin/tests/dst/Ktest.+001+54622.key bin/tests/dst/Ktest.+001+54622.private bin/tests/dst/Ktest.+003+23616.key bin/tests/dst/Ktest.+003+23616.private bin/tests/dst/Ktest.+003+49667.key bin/tests/dst/dst_2_data bin/tests/dst/t2_data_1 bin/tests/dst/t2_data_2 bin/tests/dst/t2_dsasig bin/tests/dst/t2_rsasig bin/tests/hashes/Makefile bin/tests/headerdep_test.sh bin/tests/master/Makefile bin/tests/mem/Makefile bin/tests/names/Makefile bin/tests/net/Makefile bin/tests/pkcs11/Makefile bin/tests/pkcs11/benchmarks/Makefile bin/tests/rbt/Makefile bin/tests/resolver/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/dlz/prereq.sh bin/tests/system/dlzexternal/Makefile bin/tests/system/dlzexternal/ns1/named.conf bin/tests/system/dyndb/Makefile bin/tests/system/dyndb/driver/Makefile bin/tests/system/inline/checkdsa.sh bin/tests/system/pipelined/Makefile bin/tests/system/rndc/Makefile bin/tests/system/rpz/Makefile bin/tests/system/rsabigexponent/Makefile bin/tests/system/tkey/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/virtual-time/Makefile bin/tests/virtual-time/conf.sh bin/tools/Makefile contrib/scripts/check-secure-delegation.pl contrib/scripts/zone-edit.sh doc/Makefile doc/arm/Makefile doc/arm/noteversion.xml doc/arm/pkgversion.xml doc/arm/releaseinfo.xml doc/doxygen/Doxyfile doc/doxygen/Makefile doc/doxygen/doxygen-input-filter doc/misc/Makefile doc/tex/Makefile doc/tex/armstyle.sty doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-manpage.xsl doc/xsl/isc-notes-html.xsl isc-config.sh lib/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/dns/tests/Makefile lib/irs/Makefile lib/irs/include/Makefile lib/irs/include/irs/Makefile lib/irs/include/irs/netdb.h lib/irs/include/irs/platform.h lib/irs/tests/Makefile lib/isc/$arch/Makefile lib/isc/$arch/include/Makefile lib/isc/$arch/include/isc/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/include/pk11/Makefile lib/isc/include/pkcs11/Makefile lib/isc/tests/Makefile lib/isc/nls/Makefile lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/unix/include/pkcs11/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/isccfg/tests/Makefile lib/ns/Makefile lib/ns/include/Makefile lib/ns/include/ns/Makefile lib/ns/tests/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile lib/samples/Makefile lib/samples/Makefile-postinstall unit/Makefile unit/unittest.sh"
#
"bin/tests/system/inline/checkdsa.sh") CONFIG_FILES="$CONFIG_FILES bin/tests/system/inline/checkdsa.sh" ;;
"bin/tests/system/pipelined/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/pipelined/Makefile" ;;
"bin/tests/system/rndc/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rndc/Makefile" ;;
+ "bin/tests/system/rpz/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rpz/Makefile" ;;
"bin/tests/system/rsabigexponent/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/rsabigexponent/Makefile" ;;
"bin/tests/system/tkey/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/system/tkey/Makefile" ;;
"bin/tests/tasks/Makefile") CONFIG_FILES="$CONFIG_FILES bin/tests/tasks/Makefile" ;;
echo " ECDSA algorithm support (--with-ecdsa)"
test "yes" = "$OPENSSL_ED25519" -o "$PKCS11_ED25519" && \
echo " EDDSA algorithm support (--with-eddsa)"
+ test "yes" = "$enable_dnsrps" && \
+ echo " DNS Response Policy Service interface (--enable-dnsrps)"
test "yes" = "$enable_fixed" && \
echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)"
test "yes" = "$enable_filter" && \
test "no" = "$use_geoip" && echo " GeoIP access control (--with-geoip)"
test "no" = "$use_gssapi" && echo " GSS-API (--with-gssapi)"
+ test "no" = "$enable_dnsrps" && \
+ echo " DNS Response Policy Service interface (--enable-dnsrps)"
+
test "yes" = "$enable_fixed" || \
echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)"
# Enable response policy rewriting using NS IP addresses
#
AC_ARG_ENABLE(rpz-nsip,
- [ --disable-rpz-nsip disable rpz-nsip rules [[default=enabled]]],
+ [ --disable-rpz-nsip disable rpz nsip rules [[default=enabled]]],
enable_nsip="$enableval",
enable_nsip="yes")
case "$enable_nsip" in
yes)
AC_DEFINE(ENABLE_RPZ_NSIP, 1,
- [Define to enable rpz-nsip rules.])
+ [Define to enable rpz nsip rules.])
;;
no)
;;
# Enable response policy rewriting using NS name
#
AC_ARG_ENABLE(rpz-nsdname,
- [ --disable-rpz-nsdname disable rpz-nsdname rules [[default=enabled]]],
+ [ --disable-rpz-nsdname disable rpz nsdname rules [[default=enabled]]],
enable_nsdname="$enableval",
enable_nsdname="yes")
case "$enable_nsdname" in
yes)
AC_DEFINE(ENABLE_RPZ_NSDNAME, 1,
- [Define to enable rpz-nsdname rules.])
+ [Define to enable rpz nsdname rules.])
;;
no)
;;
;;
esac
+#
+# Response policy rewriting using DNS Response Policy Service (DNSRPS)
+# interface.
+#
+# DNSRPS can be compiled into BIND everywhere with a reasonably
+# modern C compiler. It is enabled on systems with dlopen() and librpz.so.
+#
+dnsrps_avail=yes
+AC_MSG_CHECKING([for librpz __attribute__s])
+ AC_TRY_COMPILE(,[
+ extern void f(char *p __attribute__((unused)), ...)
+ __attribute__((format(printf,1,2))) __attribute__((__noreturn__));],
+ librpz_have_attr=yes
+ AC_DEFINE([LIBRPZ_HAVE_ATTR], 1, [have __attribute__s used in librpz.h])
+ AC_MSG_RESULT([yes]),
+ librpz_have_attr=no
+ AC_MSG_RESULT([no]))
+
+AC_SEARCH_LIBS(dlopen, dl)
+librpz_dl=yes
+AC_CHECK_FUNCS(dlopen dlclose dlsym,,librpz_dl=no)
+AC_ARG_ENABLE([dnsrps-dl],
+ [ --enable-dnsrps-dl DNS Response Policy Service delayed link [[default=$librpz_dl]]],
+ [enable_librpz_dl="$enableval"],
+ [enable_librpz_dl="$librpz_dl"])
+AC_ARG_WITH([dnsrps-libname],
+ [ --with-dnsrps-libname DNSRPS provider library name (librpz.so)],
+ [librpz_name="$withval"], [librpz_name="librpz.so"])
+AC_ARG_WITH([dnsrps-dir],
+ [ --with-dnsrps-dir path to DNSRPS provider library],
+ [librpz_path="$withval/$librpz_name"], [librpz_path="$librpz_name"])
+AC_DEFINE_UNQUOTED([DNSRPS_LIBRPZ_PATH], ["$librpz_path"],
+ [dnsrps $librpz_name])
+if test "x$enable_librpz_dl" = "xyes"; then
+ dnsrps_lib_open=2
+else
+ dnsrps_lib_open=1
+ # Add librpz.so to linked libraries if we are not using dlopen()
+ AC_SEARCH_LIBS([librpz_client_create], [rpz], [],
+ [dnsrps_lib_open=0
+ dnsrps_avail=no])
+fi
+AC_DEFINE_UNQUOTED([DNSRPS_LIB_OPEN], [$dnsrps_lib_open],
+ [0=no DNSRPS 1=static link 2=dlopen()])
+
+AC_ARG_ENABLE([dnsrps],
+ AS_HELP_STRING([--enable-dnsrps],
+ [enable DNS Response Policy Service API]),
+ [enable_dnsrps=$enableval],
+ [enable_dnsrps=no])
+if test "x$enable_dnsrps" != "xno"; then
+ if test "x$dnsrps_avail" != "xyes"; then
+ AC_MSG_ERROR([[dlopen and librpz.so needed for DNSRPS]])
+ fi
+ if test "x$dnsrps_lib_open" = "x0"; then
+ AC_MSG_ERROR([[dlopen and librpz.so needed for DNSRPS]])
+ fi
+ AC_DEFINE([USE_DNSRPS], [1], [Enable DNS Response Policy Service API])
+fi
+
#
# Activate "filter-aaaa-on-v4/v6" or not?
#
bin/tests/system/inline/checkdsa.sh
bin/tests/system/pipelined/Makefile
bin/tests/system/rndc/Makefile
+ bin/tests/system/rpz/Makefile
bin/tests/system/rsabigexponent/Makefile
bin/tests/system/tkey/Makefile
bin/tests/tasks/Makefile
echo " ECDSA algorithm support (--with-ecdsa)"
test "yes" = "$OPENSSL_ED25519" -o "$PKCS11_ED25519" && \
echo " EDDSA algorithm support (--with-eddsa)"
+ test "yes" = "$enable_dnsrps" && \
+ echo " DNS Response Policy Service interface (--enable-dnsrps)"
test "yes" = "$enable_fixed" && \
echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)"
test "yes" = "$enable_filter" && \
test "no" = "$use_geoip" && echo " GeoIP access control (--with-geoip)"
test "no" = "$use_gssapi" && echo " GSS-API (--with-gssapi)"
+ test "no" = "$enable_dnsrps" && \
+ echo " DNS Response Policy Service interface (--enable-dnsrps)"
+
test "yes" = "$enable_fixed" || \
echo " Allow 'fixed' rrset-order (--enable-fixed-rrset)"
[ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
[ <command>log</command> <replaceable>yes_or_no</replaceable> ]
[ <command>max-policy-ttl</command> <replaceable>number</replaceable> ]
- [ <command>min-update-interval</command> <replaceable>number</replaceable> ] ;
+ [ <command>min-update-interval</command> <replaceable>number</replaceable> ]
+ [ <command>nsip-enable <replaceable>yes_or_no</replaceable> ]
+ [ <command>nsdname-enable <replaceable>yes_or_no</replaceable> ] ;
...
<command>}</command> [ <command>recursive-only</command> <replaceable>yes_or_no</replaceable> ]
[ <command>max-policy-ttl</command> <replaceable>number</replaceable> ]
[ <command>break-dnssec</command> <replaceable>yes_or_no</replaceable> ]
[ <command>min-ns-dots</command> <replaceable>number</replaceable> ]
[ <command>nsip-wait-recurse</command> <replaceable>yes_or_no</replaceable> ]
- [ <command>qname-wait-recurse</command> <replaceable>yes_or_no</replaceable> ] ; ]
+ [ <command>qname-wait-recurse</command> <replaceable>yes_or_no</replaceable> ]
+ [ <command>nsip-enable</command> <replaceable>yes_or_no</replaceable> ]
+ [ <command>nsdname-enable</command> <replaceable>yes_or_no</replaceable> ]
+ [ <command>dnsrps-enable</command> <replaceable>yes_or_no</replaceable> ]
+ [ <command>dnsrps-options</command> { <replaceable>parameters</replaceable> } ] ; ]
[ <command>catalog-zones {</command>
<command>zone</command> <replaceable>quoted_string</replaceable>
[ <option>default-masters</option> [ <command>port</command> <replaceable>ip_port</replaceable> ] [ <command>dscp</command> <replaceable>ip_dscp</replaceable> ] <command>{</command>
NSIP triggers match IP addresses in A and
AAAA RRsets for domains that can be checked against NSDNAME
policy records.
+ The <command>nsdname-enable</command> phrase turns NSDNAME
+ triggers off or on for a single policy zone or all
+ zones.
</para>
</listitem>
</varlistentry>
least <command>min-ns-dots</command> dots.
The default value of <command>min-ns-dots</command> is
1, to exclude top level domains.
+ The <command>nsip-enable</command> phrase turns NSIP
+ triggers off or on for a single policy zone or all
+ zones.
</para>
<para>
If a name server's IP address is not yet known,
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
+ have slow authoritative servers, this behavior can cost
significant time.
- The <command>qname-wait-recurse no</command> option
- overrides that default behavior when recursion cannot
- change a non-error response.
+ The <command>qname-wait-recurse yes</command> option
+ overrides the default and enables that behavior
+ when recursion cannot change a non-error response.
The option does not affect QNAME or client-IP triggers
in policy zones listed
after other zones containing IP, NSIP and NSDNAME triggers, because
discover problems at the authoritative server.
</para>
+ <para>
+ The <command>dnsrps-enable yes</command> option turns on
+ the DNS Rsponse Policy Service (DNSRPS) interface, if it has been
+ compiled in to <command>named</command> using
+ <command>configure --enable-dnsrps</command>.
+ </para>
+
+ <para>
+ The <command>dnsrps-options</command> block provides additional
+ RPZ configuration settings, which are passed through to the
+ DNSRPS provider library.
+ Multiple DNSRPS settings in an <command>dnsrps-options</command>
+ string should be separated with semi-colons.
+ The DNSRPS provider, librpz, is passed a configuration string
+ consisting of the <command>dnsrps-options</command> text,
+ concatenated with settings derived from the
+ <command>response-policy</command> statement.
+ </para>
+
+ <para>
+ Note: The <command>dnsrps-options</command> text should only include
+ configuration settings that are specific to the DNSRPS
+ provider. For example, the DNSRPS provider from
+ Farsight Security takes options such as
+ <command>dnsrpzd-conf</command>,
+ <command>dnsrpzd-sock</command>, and
+ <command>dnzrpzd-args</command> (for details of these options,
+ see the <command>librpz</command> documentation).
+ Other RPZ configuration settings could be included in
+ <command>dnsrps-options</command>
+ as well, but if <command>named</command> were switched
+ back to traditional RPZ by setting
+ <command>dnsrps-enable</command> to "no", those options would
+ be ignored.
+ </para>
+
<para>
The TTL of a record modified by RPZ policies is set from the
TTL of the relevant record in policy zone. It is then limited
<section xml:id="relnotes_features"><info><title>New Features</title></info>
<itemizedlist>
+ <listitem>
+ <para>
+ Added support for the DNS Response Policy Service (DNSRPS) API,
+ a mechanism to allow <command>named</command> to use an external
+ response policy provider. (One example of such a provider is
+ "FastRPZ" from Farsight Security, Inc.) This allows the same
+ types of policy filtering as standard RPZ, but can reduce the
+ workload for <command>named</command>, particularly when using
+ large and frequently-updated policy zones. It also enables
+ <command>named</command> to share response policy providers
+ with other DNS implementations such as Unbound.
+ </para>
+ <para>
+ This feature is avaiable if BIND is built with
+ <command>configure --enable-dnsrps</command>
+ and if <command>dnsrps-enable</command> is set to "yes" in
+ <filename>named.conf</filename>.
+ </para>
+ <para>
+ Thanks to Vernon Schryver and Farsight Security for the
+ contribution. [RT #43376]
+ </para>
+ </listitem>
<listitem>
<para>
Code implementing name server query processing has been moved
DNSOBJS = acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \
cache.@O@ callbacks.@O@ catz.@O@ clientinfo.@O@ compress.@O@ \
db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
- dlz.@O@ dns64.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ ecs.@O@ \
- forward.@O@ ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
+ dlz.@O@ dns64.@O@ dnsrps.@O@ dnssec.@O@ ds.@O@ dyndb.@O@ \
+ ecs.@O@ forward.@O@ \
+ ipkeylist.@O@ iptable.@O@ journal.@O@ keydata.@O@ \
keytable.@O@ lib.@O@ log.@O@ lookup.@O@ \
master.@O@ masterdump.@O@ message.@O@ \
name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ nta.@O@ \
DNSSRCS = acl.c adb.c badcache. byaddr.c \
cache.c callbacks.c clientinfo.c compress.c \
db.c dbiterator.c dbtable.c diff.c dispatch.c \
- dlz.c dns64.c dnssec.c ds.c dyndb.c ecs.c forward.c \
+ dlz.c dns64.c dnsrps.c dnssec.c ds.c dyndb.c ecs.c forward.c \
ipkeylist.c iptable.c journal.c keydata.c keytable.c lib.c \
log.c lookup.c master.c masterdump.c message.c \
name.c ncache.c nsec.c nsec3.c nta.c \
--- /dev/null
+/*
+ * Copyright (C) 2017 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 */
+
+#include <config.h>
+
+#ifdef USE_DNSRPS
+
+#include <isc/mem.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+
+#include <dns/db.h>
+#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN
+#include <dns/dnsrps.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/rpz.h>
+
+librpz_t *librpz;
+librpz_emsg_t librpz_lib_open_emsg;
+static void *librpz_handle;
+
+#define RPSDB_MAGIC ISC_MAGIC('R', 'P', 'Z', 'F')
+#define VALID_RPSDB(rpsdb) ((rpsdb)->common.impmagic == RPSDB_MAGIC)
+
+#define RD_DB(r) ((r)->private1)
+#define RD_CUR_RR(r) ((r)->private2)
+#define RD_NEXT_RR(r) ((r)->resign)
+#define RD_COUNT(r) ((r)->privateuint4)
+
+typedef struct {
+ dns_rdatasetiter_t common;
+ dns_rdatatype_t type;
+ dns_rdataclass_t class;
+ uint32_t ttl;
+ uint count;
+ librpz_idx_t next_rr;
+} rpsdb_rdatasetiter_t;
+
+static dns_dbmethods_t rpsdb_db_methods;
+static dns_rdatasetmethods_t rpsdb_rdataset_methods;
+static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods;
+
+static librpz_clist_t *clist;
+
+static isc_mutex_t dnsrps_mutex;
+
+static void
+dnsrps_lock(void *mutex0) {
+ isc_mutex_t *mutex = mutex0;
+
+ LOCK(mutex);
+}
+
+static void
+dnsrps_unlock(void *mutex0) {
+ isc_mutex_t *mutex = mutex0;
+
+ UNLOCK(mutex);
+}
+
+static void
+dnsrps_mutex_destroy(void *mutex0) {
+ isc_mutex_t *mutex = mutex0;
+
+ DESTROYLOCK(mutex);
+}
+
+static void
+dnsrps_log_fnc(librpz_log_level_t level, void *ctxt, const char *buf) {
+ int isc_level;
+
+ UNUSED(ctxt);
+
+ /* Setting librpz_log_level in the configuration overrides the
+ * BIND9 logging levels. */
+ if (level > LIBRPZ_LOG_TRACE1 &&
+ level <= librpz->log_level_val(LIBRPZ_LOG_INVALID))
+ level = LIBRPZ_LOG_TRACE1;
+
+ switch(level) {
+ case LIBRPZ_LOG_FATAL:
+ case LIBRPZ_LOG_ERROR: /* errors */
+ default:
+ isc_level = DNS_RPZ_ERROR_LEVEL;
+ break;
+
+ case LIBRPZ_LOG_TRACE1: /* big events such as dnsrpzd starts */
+ isc_level = DNS_RPZ_INFO_LEVEL;
+ break;
+
+ case LIBRPZ_LOG_TRACE2: /* smaller dnsrpzd zone transfers */
+ isc_level = DNS_RPZ_DEBUG_LEVEL1;
+ break;
+
+ case LIBRPZ_LOG_TRACE3: /* librpz hits */
+ isc_level = DNS_RPZ_DEBUG_LEVEL2;
+ break;
+
+ case LIBRPZ_LOG_TRACE4: /* librpz lookups */
+ isc_level = DNS_RPZ_DEBUG_LEVEL3;
+ break;
+ }
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
+ isc_level, "dnsrps: %s", buf);
+}
+
+/*
+ * Start dnsrps for the entire server.
+ * This is not thread safe, but it is called by a single thread.
+ */
+isc_result_t
+dns_dnsrps_server_create(void) {
+ librpz_emsg_t emsg;
+ isc_result_t result;
+
+ INSIST(clist == NULL);
+ INSIST(librpz == NULL);
+ INSIST(librpz_handle == NULL);
+
+ /*
+ * Notice if librpz is available.
+ */
+ librpz = librpz_lib_open(&librpz_lib_open_emsg,
+ &librpz_handle, DNSRPS_LIBRPZ_PATH);
+ /*
+ * Stop now without complaining if librpz is not available.
+ * Complain later if and when librpz is needed for a view with
+ * "dnsrps-enable yse" (including the default view).
+ */
+ if (librpz == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = isc_mutex_init(&dnsrps_mutex);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ librpz->set_log(&dnsrps_log_fnc, NULL);
+
+ clist = librpz->clist_create(&emsg, dnsrps_lock, dnsrps_unlock,
+ dnsrps_mutex_destroy, &dnsrps_mutex,
+ dns_lctx);
+ if (clist == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dnsrps: %s", emsg.c);
+ return (ISC_R_NOMEMORY);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Stop dnsrps for the entire server.
+ * This is not thread safe.
+ */
+void
+dns_dnsrps_server_destroy(void) {
+ if (clist != NULL)
+ librpz->clist_detach(&clist);
+
+#ifdef LIBRPZ_USE_DLOPEN
+ if (librpz != NULL) {
+ INSIST(librpz_handle != NULL);
+ if (dlclose(librpz_handle) != 0)
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "dnsrps: dlclose(): %s", dlerror());
+ librpz_handle = NULL;
+ }
+#endif
+}
+
+/*
+ * Ready dnsrps for a view.
+ */
+isc_result_t
+dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr) {
+ librpz_emsg_t emsg;
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_DEBUG_LEVEL3,
+ "dnsrps configuration \"%s\"", rps_cstr);
+
+ new->rps_client = librpz->client_create(&emsg, clist,
+ rps_cstr, ISC_FALSE);
+ if (new->rps_client == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "librpz->client_create(): %s", emsg.c);
+ new->p.dnsrps_enabled = ISC_FALSE;
+ return (ISC_R_FAILURE);
+ }
+
+ new->p.dnsrps_enabled = ISC_TRUE;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Connect to and start the dnsrps daemon, dnsrpzd.
+ */
+isc_result_t
+dns_dnsrps_connect(dns_rpz_zones_t *rpzs) {
+ librpz_emsg_t emsg;
+
+ if (rpzs == NULL || !rpzs->p.dnsrps_enabled)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Fail only if we failed to link to librpz.
+ */
+ if (librpz == NULL) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "librpz->connect(): %s", librpz_lib_open_emsg.c);
+ return (ISC_R_FAILURE);
+ }
+
+ if (!librpz->connect(&emsg, rpzs->rps_client, true)) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+ "librpz->connect(): %s", emsg.c);
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, DNS_LOGMODULE_RBTDB,
+ DNS_RPZ_INFO_LEVEL, "dnsrps: librpz version %s",
+ librpz->version);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Get ready to try RPZ rewriting.
+ */
+isc_result_t
+dns_dnsrps_rewrite_init(librpz_emsg_t *emsg, dns_rpz_st_t *st,
+ dns_rpz_zones_t *rpzs, const dns_name_t *qname,
+ isc_mem_t *mctx, isc_boolean_t have_rd)
+{
+ rpsdb_t *rpsdb;
+
+ rpsdb = isc_mem_get(mctx, sizeof(*rpsdb));
+ if (rpsdb == NULL) {
+ strlcpy(emsg->c, "no memory", sizeof(emsg->c));
+ return (ISC_R_NOMEMORY);
+ }
+ memset(rpsdb, 0, sizeof(*rpsdb));
+
+ if (!librpz->rsp_create(emsg, &rpsdb->rsp, NULL,
+ rpzs->rps_client, have_rd, false)) {
+ isc_mem_put(mctx, rpsdb, sizeof(*rpsdb));
+ return (DNS_R_SERVFAIL);
+ }
+ if (rpsdb->rsp == NULL) {
+ isc_mem_put(mctx, rpsdb, sizeof(*rpsdb));
+ return (DNS_R_DISALLOWED);
+ }
+
+ rpsdb->common.magic = DNS_DB_MAGIC;
+ rpsdb->common.impmagic = RPSDB_MAGIC;
+ rpsdb->common.methods = &rpsdb_db_methods;
+ rpsdb->common.rdclass = dns_rdataclass_in;
+ dns_name_init(&rpsdb->common.origin, NULL);
+ isc_mem_attach(mctx, &rpsdb->common.mctx);
+
+ rpsdb->ref_cnt = 1;
+ rpsdb->qname = qname;
+
+ st->rpsdb = &rpsdb->common;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Convert a dnsrps policy to a classic BIND9 RPZ policy.
+ */
+dns_rpz_policy_t
+dns_dnsrps_2policy(librpz_policy_t rps_policy) {
+ switch (rps_policy) {
+ case LIBRPZ_POLICY_UNDEFINED:
+ return (DNS_RPZ_POLICY_MISS);
+ case LIBRPZ_POLICY_PASSTHRU:
+ return (DNS_RPZ_POLICY_PASSTHRU);
+ case LIBRPZ_POLICY_DROP:
+ return (DNS_RPZ_POLICY_DROP);
+ case LIBRPZ_POLICY_TCP_ONLY:
+ return (DNS_RPZ_POLICY_TCP_ONLY);
+ case LIBRPZ_POLICY_NXDOMAIN:
+ return (DNS_RPZ_POLICY_NXDOMAIN);
+ case LIBRPZ_POLICY_NODATA:
+ return (DNS_RPZ_POLICY_NODATA);
+ case LIBRPZ_POLICY_RECORD:
+ case LIBRPZ_POLICY_CNAME:
+ return (DNS_RPZ_POLICY_RECORD);
+
+ case LIBRPZ_POLICY_DELETED:
+ case LIBRPZ_POLICY_GIVEN:
+ case LIBRPZ_POLICY_DISABLED:
+ default:
+ INSIST(0);
+ }
+}
+
+/*
+ * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type.
+ */
+dns_rpz_type_t
+dns_dnsrps_trig2type(librpz_trig_t trig) {
+ switch (trig) {
+ case LIBRPZ_TRIG_BAD:
+ default:
+ return (DNS_RPZ_TYPE_BAD);
+ case LIBRPZ_TRIG_CLIENT_IP:
+ return (DNS_RPZ_TYPE_CLIENT_IP);
+ case LIBRPZ_TRIG_QNAME:
+ return (DNS_RPZ_TYPE_QNAME);
+ case LIBRPZ_TRIG_IP:
+ return (DNS_RPZ_TYPE_IP);
+ case LIBRPZ_TRIG_NSDNAME:
+ return (DNS_RPZ_TYPE_NSDNAME);
+ case LIBRPZ_TRIG_NSIP:
+ return (DNS_RPZ_TYPE_NSIP);
+ }
+}
+
+/*
+ * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type.
+ */
+librpz_trig_t
+dns_dnsrps_type2trig(dns_rpz_type_t type) {
+ switch (type) {
+ case DNS_RPZ_TYPE_BAD:
+ default:
+ return (LIBRPZ_TRIG_BAD);
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ return (LIBRPZ_TRIG_CLIENT_IP);
+ case DNS_RPZ_TYPE_QNAME:
+ return (LIBRPZ_TRIG_QNAME);
+ case DNS_RPZ_TYPE_IP:
+ return (LIBRPZ_TRIG_IP);
+ case DNS_RPZ_TYPE_NSDNAME:
+ return (LIBRPZ_TRIG_NSDNAME);
+ case DNS_RPZ_TYPE_NSIP:
+ return (LIBRPZ_TRIG_NSIP);
+ }
+}
+
+static void
+rpsdb_attach(dns_db_t *source, dns_db_t **targetp) {
+ rpsdb_t *rpsdb = (rpsdb_t *)source;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+
+ /*
+ * Use a simple count because only one thread uses any single rpsdb_t
+ */
+ ++rpsdb->ref_cnt;
+ *targetp = source;
+}
+
+static void
+rpsdb_detach(dns_db_t **dbp) {
+ rpsdb_t *rpsdb = (rpsdb_t *)*dbp;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(rpsdb->ref_cnt > 0);
+
+ *dbp = NULL;
+
+ /*
+ * Simple count because only one thread uses a rpsdb_t.
+ */
+ if (--rpsdb->ref_cnt != 0)
+ return;
+
+ librpz->rsp_detach(&rpsdb->rsp);
+ rpsdb->common.impmagic = 0;
+ isc_mem_putanddetach(&rpsdb->common.mctx, rpsdb, sizeof(*rpsdb));
+}
+
+static void
+rpsdb_attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+ REQUIRE(source == &rpsdb->origin_node ||
+ source == &rpsdb->data_node);
+
+ /*
+ * Simple count because only one thread uses a rpsdb_t.
+ */
+ ++rpsdb->ref_cnt;
+ *targetp = source;
+}
+
+static void
+rpsdb_detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(*targetp == &rpsdb->origin_node ||
+ *targetp == &rpsdb->data_node);
+
+ *targetp = NULL;
+ rpsdb_detach(&db);
+}
+
+static isc_result_t
+rpsdb_findnode(dns_db_t *db, const dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+ dns_db_t *dbp;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+ REQUIRE(!create);
+
+ /*
+ * A fake/shim rpsdb has two nodes.
+ * One is the origin to support query_addsoa() in bin/named/query.c.
+ * The other contains rewritten RRs.
+ */
+ if (dns_name_equal(name, &db->origin))
+ *nodep = &rpsdb->origin_node;
+ else
+ *nodep = &rpsdb->data_node;
+ dbp = NULL;
+ rpsdb_attach(db, &dbp);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rpsdb_bind_rdataset(dns_rdataset_t *rdataset, uint count, librpz_idx_t next_rr,
+ dns_rdatatype_t type, uint16_t class, uint32_t ttl,
+ rpsdb_t *rpsdb)
+{
+ dns_db_t *dbp;
+
+ INSIST(rdataset->methods == NULL); /* We must be disassociated. */
+ REQUIRE(type != dns_rdatatype_none);
+
+ rdataset->methods = &rpsdb_rdataset_methods;
+ rdataset->rdclass = class;
+ rdataset->type = type;
+ rdataset->ttl = ttl;
+ dbp = NULL;
+ dns_db_attach(&rpsdb->common, &dbp);
+ RD_DB(rdataset) = dbp;
+ RD_COUNT(rdataset) = count;
+ RD_NEXT_RR(rdataset) = next_rr;
+ RD_CUR_RR(rdataset) = NULL;
+}
+
+static isc_result_t
+rpsdb_bind_soa(dns_rdataset_t *rdataset, rpsdb_t *rpsdb) {
+ uint32_t ttl;
+ librpz_emsg_t emsg;
+
+ if (!librpz->rsp_soa(&emsg, &ttl, NULL, NULL,
+ &rpsdb->result, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ rpsdb_bind_rdataset(rdataset, 1, LIBRPZ_IDX_BAD, dns_rdatatype_soa,
+ dns_rdataclass_in, ttl, rpsdb);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Forge an rdataset of the desired type from a librpz result.
+ * This is written for simplicity instead of speed, because RPZ rewriting
+ * should be rare compared to normal BIND operations.
+ */
+static isc_result_t
+rpsdb_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_stdtime_t now, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+ dns_rdatatype_t foundtype;
+ dns_rdataclass_t class;
+ uint32_t ttl;
+ uint count;
+ librpz_emsg_t emsg;
+
+ UNUSED(version);
+ UNUSED(covers);
+ UNUSED(now);
+ UNUSED(sigrdataset);
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+
+ if (node == &rpsdb->origin_node) {
+ if (type == dns_rdatatype_any)
+ return (ISC_R_SUCCESS);
+ if (type == dns_rdatatype_soa)
+ return (rpsdb_bind_soa(rdataset, rpsdb));
+ return (DNS_R_NXRRSET);
+ }
+
+ REQUIRE(node == &rpsdb->data_node);
+
+ switch (rpsdb->result.policy) {
+ case LIBRPZ_POLICY_UNDEFINED:
+ case LIBRPZ_POLICY_DELETED:
+ case LIBRPZ_POLICY_PASSTHRU:
+ case LIBRPZ_POLICY_DROP:
+ case LIBRPZ_POLICY_TCP_ONLY:
+ case LIBRPZ_POLICY_GIVEN:
+ case LIBRPZ_POLICY_DISABLED:
+ default:
+ librpz->log(LIBRPZ_LOG_ERROR, NULL,
+ "impossible dnsrps policy %d at %s:%d",
+ rpsdb->result.policy, __FILE__, __LINE__);
+ return (DNS_R_SERVFAIL);
+
+ case LIBRPZ_POLICY_NXDOMAIN:
+ return (DNS_R_NXDOMAIN);
+
+ case LIBRPZ_POLICY_NODATA:
+ return (DNS_R_NXRRSET);
+
+ case LIBRPZ_POLICY_RECORD:
+ case LIBRPZ_POLICY_CNAME:
+ break;
+ }
+
+ if (type == dns_rdatatype_soa)
+ return (rpsdb_bind_soa(rdataset, rpsdb));
+
+ /*
+ * There is little to do for an ANY query.
+ */
+ if (type == dns_rdatatype_any)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Reset to the start of the RRs.
+ * This function is only used after a policy has been chosen,
+ * and so without caring whether it is after recursion.
+ */
+ if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ if (!librpz->rsp_rr(&emsg, &foundtype, &class, &ttl, NULL,
+ &rpsdb->result, rpsdb->qname->ndata,
+ rpsdb->qname->length, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ REQUIRE(foundtype != dns_rdatatype_none);
+
+ /*
+ * Ho many of the target RR type are available?
+ */
+ count = 0;
+ do {
+ if (type == foundtype || type == dns_rdatatype_any)
+ ++count;
+
+ if (!librpz->rsp_rr(&emsg, &foundtype, NULL, NULL, NULL,
+ &rpsdb->result, rpsdb->qname->ndata,
+ rpsdb->qname->length, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ } while (foundtype != dns_rdatatype_none);
+ if (count == 0)
+ return (DNS_R_NXRRSET);
+ rpsdb_bind_rdataset(rdataset, count, rpsdb->result.next_rr,
+ type, class, ttl, rpsdb);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rpsdb_finddb(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_dbnode_t *node;
+ isc_result_t result;
+
+ UNUSED(version);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(sigrdataset);
+
+ if (nodep == NULL) {
+ node = NULL;
+ nodep = &node;
+ }
+ rpsdb_findnode(db, name, false, nodep);
+ result = dns_name_copy(name, foundname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (rpsdb_findrdataset(db, *nodep, NULL, type, 0, 0,
+ rdataset, sigrdataset));
+}
+
+static isc_result_t
+rpsdb_allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+ rpsdb_rdatasetiter_t *rpsdb_iter;
+
+ UNUSED(version);
+ UNUSED(now);
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(node == &rpsdb->origin_node || node == &rpsdb->data_node);
+
+ rpsdb_iter = isc_mem_get(rpsdb->common.mctx, sizeof(*rpsdb_iter));
+ if (rpsdb_iter == NULL)
+ return (ISC_R_NOMEMORY);
+
+ memset(rpsdb_iter, 0, sizeof(*rpsdb_iter));
+ rpsdb_iter->common.magic = DNS_RDATASETITER_MAGIC;
+ rpsdb_iter->common.methods = &rpsdb_rdatasetiter_methods;
+ rpsdb_iter->common.db = db;
+ rpsdb_attachnode(db, node, &rpsdb_iter->common.node);
+
+ *iteratorp = &rpsdb_iter->common;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+rpsdb_issecure(dns_db_t *db) {
+ UNUSED(db);
+
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+rpsdb_getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
+ rpsdb_t *rpsdb = (rpsdb_t *)db;
+
+ REQUIRE(VALID_RPSDB(rpsdb));
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ rpsdb_attachnode(db, &rpsdb->origin_node, nodep);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rpsdb_rdataset_disassociate(dns_rdataset_t *rdataset) {
+ dns_db_t *db;
+
+ /*
+ * Detach the last RR delivered.
+ */
+ if (RD_CUR_RR(rdataset) != NULL) {
+ free(RD_CUR_RR(rdataset));
+ RD_CUR_RR(rdataset) = NULL;
+ }
+
+ db = RD_DB(rdataset);
+ RD_DB(rdataset) = NULL;
+ dns_db_detach(&db);
+}
+
+static isc_result_t
+rpsdb_rdataset_next(dns_rdataset_t *rdataset) {
+ rpsdb_t *rpsdb;
+ uint16_t type;
+ dns_rdataclass_t class;
+ librpz_rr_t *rr;
+ librpz_emsg_t emsg;
+
+ rpsdb = RD_DB(rdataset);
+
+ /*
+ * Detach the previous RR.
+ */
+ if (RD_CUR_RR(rdataset) != NULL) {
+ free(RD_CUR_RR(rdataset));
+ RD_CUR_RR(rdataset) = NULL;
+ }
+
+ /*
+ * Get the next RR of the specified type.
+ * SOAs differ.
+ */
+ if (rdataset->type == dns_rdatatype_soa) {
+ if (RD_NEXT_RR(rdataset) == LIBRPZ_IDX_NULL)
+ return (ISC_R_NOMORE);
+ RD_NEXT_RR(rdataset) = LIBRPZ_IDX_NULL;
+ if (!librpz->rsp_soa(&emsg, NULL, &rr, NULL,
+ &rpsdb->result, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ RD_CUR_RR(rdataset) = rr;
+ return (ISC_R_SUCCESS);
+ }
+
+ rpsdb->result.next_rr = RD_NEXT_RR(rdataset);
+ for (;;) {
+ if (!librpz->rsp_rr(&emsg, &type, &class, NULL, &rr,
+ &rpsdb->result, rpsdb->qname->ndata,
+ rpsdb->qname->length, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ if (rdataset->type == type &&
+ rdataset->rdclass == class) {
+ RD_CUR_RR(rdataset) = rr;
+ RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
+ return (ISC_R_SUCCESS);
+ }
+ if (type == dns_rdatatype_none)
+ return (ISC_R_NOMORE);
+ free(rr);
+ }
+}
+
+static isc_result_t
+rpsdb_rdataset_first(dns_rdataset_t *rdataset) {
+ rpsdb_t *rpsdb;
+ librpz_emsg_t emsg;
+
+ rpsdb = RD_DB(rdataset);
+ REQUIRE(VALID_RPSDB(rpsdb));
+
+ if (RD_CUR_RR(rdataset) != NULL) {
+ free(RD_CUR_RR(rdataset));
+ RD_CUR_RR(rdataset) = NULL;
+ }
+
+ if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ if (rdataset->type == dns_rdatatype_soa)
+ RD_NEXT_RR(rdataset) = LIBRPZ_IDX_BAD;
+ else
+ RD_NEXT_RR(rdataset) = rpsdb->result.next_rr;
+
+ return (rpsdb_rdataset_next(rdataset));
+}
+
+static void
+rpsdb_rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata) {
+ rpsdb_t *rpsdb;
+ librpz_rr_t *rr;
+ isc_region_t r;
+
+ rpsdb = RD_DB(rdataset);
+ REQUIRE(VALID_RPSDB(rpsdb));
+ rr = RD_CUR_RR(rdataset);
+ REQUIRE(rr != NULL);
+
+ r.length = ntohs(rr->rdlength);
+ r.base = rr->rdata;
+ dns_rdata_fromregion(rdata, ntohs(rr->class), ntohs(rr->type), &r);
+}
+
+static void
+rpsdb_rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ rpsdb_t *rpsdb;
+ dns_db_t *dbp;
+
+ INSIST(!ISC_LINK_LINKED(target, link));
+ *target = *source;
+ ISC_LINK_INIT(target, link);
+ rpsdb = RD_DB(source);
+ REQUIRE(VALID_RPSDB(rpsdb));
+ dbp = NULL;
+ dns_db_attach(&rpsdb->common, &dbp);
+ RD_DB(target) = dbp;
+ RD_CUR_RR(target) = NULL;
+ RD_NEXT_RR(target) = LIBRPZ_IDX_NULL;
+}
+
+static unsigned int
+rpsdb_rdataset_count(dns_rdataset_t *rdataset) {
+ rpsdb_t *rpsdb;
+
+ rpsdb = RD_DB(rdataset);
+ REQUIRE(VALID_RPSDB(rpsdb));
+
+ return (RD_COUNT(rdataset));
+}
+
+static void
+rpsdb_rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+ rpsdb_t *rpsdb;
+ dns_rdatasetiter_t *iterator;
+ isc_mem_t *mctx;
+
+ iterator = *iteratorp;
+ rpsdb = (rpsdb_t *)iterator->db;
+ REQUIRE(VALID_RPSDB(rpsdb));
+
+ mctx = iterator->db->mctx;
+ dns_db_detachnode(iterator->db, &iterator->node);
+ isc_mem_put(mctx, iterator, sizeof(rpsdb_rdatasetiter_t));
+ *iteratorp = NULL;
+}
+
+static isc_result_t
+rpsdb_rdatasetiter_next(dns_rdatasetiter_t *iter) {
+ rpsdb_t *rpsdb;
+ rpsdb_rdatasetiter_t *rpsdb_iter;
+ dns_rdatatype_t next_type, type;
+ dns_rdataclass_t next_class, class;
+ uint32_t ttl;
+ librpz_emsg_t emsg;
+
+ rpsdb = (rpsdb_t *)iter->db;
+ REQUIRE(VALID_RPSDB(rpsdb));
+ rpsdb_iter = (rpsdb_rdatasetiter_t *)iter;
+
+ /*
+ * This function is only used after a policy has been chosen,
+ * and so without caring whether it is after recursion.
+ */
+ if (!librpz->rsp_result(&emsg, &rpsdb->result, true, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ /*
+ * Find the next class and type after the current class and type
+ * among the RRs in current result.
+ * As a side effect, count the number of those RRs.
+ */
+ rpsdb_iter->count = 0;
+ next_class = dns_rdataclass_reserved0;
+ next_type = dns_rdatatype_none;
+ for (;;) {
+ if (!librpz->rsp_rr(&emsg, &type, &class, &ttl,
+ NULL, &rpsdb->result, rpsdb->qname->ndata,
+ rpsdb->qname->length, rpsdb->rsp)) {
+ librpz->log(LIBRPZ_LOG_ERROR, NULL, "%s", emsg.c);
+ return (DNS_R_SERVFAIL);
+ }
+ if (type == dns_rdatatype_none) {
+ if (next_type == dns_rdatatype_none)
+ return (ISC_R_NOMORE);
+ rpsdb_iter->type = next_type;
+ rpsdb_iter->class = next_class;
+ return (ISC_R_SUCCESS);
+ }
+ /*
+ * Skip RRs with the current class and type or before.
+ */
+ if (rpsdb_iter->class > class ||
+ (rpsdb_iter->class = class && rpsdb_iter->type >= type))
+ continue;
+ if (next_type == dns_rdatatype_none ||
+ next_class > class ||
+ (next_class == class && next_type > type)) {
+ /*
+ * This is the first of a subsequent class and type.
+ */
+ next_type = type;
+ next_class = class;
+ rpsdb_iter->ttl = ttl;
+ rpsdb_iter->count = 1;
+ rpsdb_iter->next_rr = rpsdb->result.next_rr;
+ } else if (next_type == type && next_class == class) {
+ ++rpsdb_iter->count;
+ }
+ }
+}
+
+static isc_result_t
+rpsdb_rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ rpsdb_t *rpsdb;
+ rpsdb_rdatasetiter_t *rpsdb_iter;
+
+ rpsdb = (rpsdb_t *)iterator->db;
+ REQUIRE(VALID_RPSDB(rpsdb));
+ rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator;
+
+ rpsdb_iter->type = dns_rdatatype_none;
+ rpsdb_iter->class = dns_rdataclass_reserved0;
+ return (rpsdb_rdatasetiter_next(iterator));
+}
+
+static void
+rpsdb_rdatasetiter_current(dns_rdatasetiter_t *iterator,
+ dns_rdataset_t *rdataset)
+{
+ rpsdb_t *rpsdb;
+ rpsdb_rdatasetiter_t *rpsdb_iter;
+
+ rpsdb = (rpsdb_t *)iterator->db;
+ REQUIRE(VALID_RPSDB(rpsdb));
+ rpsdb_iter = (rpsdb_rdatasetiter_t *)iterator;
+ REQUIRE(rpsdb_iter->type != dns_rdatatype_none);
+
+ rpsdb_bind_rdataset(rdataset,
+ rpsdb_iter->count, rpsdb_iter->next_rr,
+ rpsdb_iter->type, rpsdb_iter->class,
+ rpsdb_iter->ttl, rpsdb);
+}
+
+static dns_dbmethods_t rpsdb_db_methods = {
+ rpsdb_attach,
+ rpsdb_detach,
+ NULL, /* beginload */
+ NULL, /* endload */
+ NULL, /* serialize */
+ NULL, /* dump */
+ NULL, /* currentversion */
+ NULL, /* newversion */
+ NULL, /* attachversion */
+ NULL, /* closeversion */
+ rpsdb_findnode,
+ rpsdb_finddb,
+ NULL, /* findzonecut*/
+ rpsdb_attachnode,
+ rpsdb_detachnode,
+ NULL, /* expirenode */
+ NULL, /* printnode */
+ NULL, /* createiterator */
+ rpsdb_findrdataset,
+ rpsdb_allrdatasets,
+ NULL, /* addrdataset */
+ NULL, /* subtractrdataset */
+ NULL, /* deleterdataset */
+ rpsdb_issecure,
+ NULL, /* nodecount */
+ NULL, /* ispersistent */
+ NULL, /* overmem */
+ NULL, /* settask */
+ rpsdb_getoriginnode,
+ NULL, /* transfernode */
+ NULL, /* getnsec3parameters */
+ NULL, /* findnsec3node */
+ NULL, /* setsigningtime */
+ NULL, /* getsigningtime */
+ NULL, /* resigned */
+ NULL, /* isdnssec */
+ NULL, /* getrrsetstats */
+ NULL, /* rpz_attach */
+ NULL, /* rpz_ready */
+ NULL, /* findnodeext */
+ NULL, /* findext */
+ NULL, /* setcachestats */
+ NULL, /* hashsize */
+ NULL, /* nodefullname */
+ NULL /* getsize */
+};
+
+static dns_rdatasetmethods_t rpsdb_rdataset_methods = {
+ rpsdb_rdataset_disassociate,
+ rpsdb_rdataset_first,
+ rpsdb_rdataset_next,
+ rpsdb_rdataset_current,
+ rpsdb_rdataset_clone,
+ rpsdb_rdataset_count,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static dns_rdatasetitermethods_t rpsdb_rdatasetiter_methods = {
+ rpsdb_rdatasetiter_destroy,
+ rpsdb_rdatasetiter_first,
+ rpsdb_rdatasetiter_next,
+ rpsdb_rdatasetiter_current
+};
+
+#endif /* USE_DNSRPS */
cache.h callbacks.h catz.h cert.h \
client.h clientinfo.h compress.h \
db.h dbiterator.h dbtable.h diff.h dispatch.h \
- dlz.h dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h \
+ dlz.h dlz_dlopen.h dns64.h dnsrps.h dnssec.h ds.h dsdigest.h \
dnstap.h dyndb.h ecs.h \
edns.h ecdb.h events.h fixedname.h forward.h geoip.h \
ipkeylist.h iptable.h \
journal.h keydata.h keyflags.h keytable.h keyvalues.h \
- lib.h lookup.h log.h master.h masterdump.h message.h \
+ lib.h librpz.h lookup.h log.h master.h masterdump.h message.h \
name.h ncache.h nsec.h nsec3.h nta.h opcode.h order.h \
peer.h portlist.h private.h \
rbt.h rcode.h rdata.h rdataclass.h rdatalist.h \
--- /dev/null
+#ifndef DNS_DNSRPS_H
+#define DNS_DNSRPS_H
+
+#include <isc/lang.h>
+#include <dns/types.h>
+
+#include <config.h>
+
+#ifdef USE_DNSRPS
+
+#include <dns/librpz.h>
+#include <dns/rpz.h>
+
+/*
+ * Error message if dlopen(librpz) failed.
+ */
+extern librpz_emsg_t librpz_lib_open_emsg;
+
+
+/*
+ * These shim BIND9 database, node, and rdataset are handles on RRs from librpz.
+ *
+ * All of these structures are used by a single thread and so need no locks.
+ *
+ * rpsdb_t holds the state for a set of RPZ queries.
+ *
+ * rpsnode_t is a link to the rpsdb_t for the set of RPZ queries
+ * and a flag saying whether it is pretending to be a node with RRs for
+ * the qname or the node with the SOA for the zone containing the rewritten
+ * RRs or justifying NXDOMAIN.
+ */
+typedef struct {
+ uint8_t unused;
+} rpsnode_t;
+typedef struct rpsdb {
+ dns_db_t common;
+ int ref_cnt;
+ librpz_result_id_t hit_id;
+ librpz_result_t result;
+ librpz_rsp_t* rsp;
+ librpz_domain_buf_t origin_buf;
+ const dns_name_t *qname;
+ rpsnode_t origin_node;
+ rpsnode_t data_node;
+} rpsdb_t;
+
+
+/*
+ * Convert a dnsrps policy to a classic BIND9 RPZ policy.
+ */
+dns_rpz_policy_t dns_dnsrps_2policy(librpz_policy_t rps_policy);
+
+/*
+ * Convert a dnsrps trigger to a classic BIND9 RPZ rewrite or trigger type.
+ */
+dns_rpz_type_t dns_dnsrps_trig2type(librpz_trig_t trig);
+
+/*
+ * Convert a classic BIND9 RPZ rewrite or trigger type to a librpz trigger type.
+ */
+librpz_trig_t dns_dnsrps_type2trig(dns_rpz_type_t type);
+
+/*
+ * Start dnsrps for the entire server.
+ */
+isc_result_t dns_dnsrps_server_create(void);
+
+/*
+ * Stop dnsrps for the entire server.
+ */
+void dns_dnsrps_server_destroy(void);
+
+/*
+ * Ready dnsrps for a view.
+ */
+isc_result_t dns_dnsrps_view_init(dns_rpz_zones_t *new, char *rps_cstr);
+
+/*
+ * Connect to and start the dnsrps daemon, dnsrpzd.
+ */
+isc_result_t dns_dnsrps_connect(dns_rpz_zones_t *rpzs);
+
+/*
+ * Get ready to try dnsrps rewriting.
+ */
+isc_result_t dns_dnsrps_rewrite_init(librpz_emsg_t *emsg, dns_rpz_st_t *st,
+ dns_rpz_zones_t *rpzs,
+ const dns_name_t *qname, isc_mem_t *mctx,
+ isc_boolean_t have_rd);
+
+#endif /* USE_DNSRPS */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_DNSRPS_H */
--- /dev/null
+/*
+ * Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Define the interface from a DNS resolver to the Response Policy Zone
+ * library, librpz.
+ *
+ * This file should be included only the interface functions between the
+ * resolver and librpz to avoid name space pollution.
+ *
+ * Copyright (c) 2016-2017 Farsight Security, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * version 1.2.12
+ */
+
+#ifndef LIBRPZ_H
+#define LIBRPZ_H
+
+#include <arpa/nameser.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+
+/*
+ * Allow either ordinary or dlopen() linking.
+ */
+#ifdef LIBRPZ_INTERNAL
+#define LIBDEF(t,s) extern t s;
+#define LIBDEF_F(f) LIBDEF(librpz_##f##_t, librpz_##f)
+#else
+#define LIBDEF(t,s)
+#define LIBDEF_F(f)
+#endif
+
+/*
+ * Response Policy Zone triggers.
+ * Comparisons of trigger precedences require
+ * LIBRPZ_TRIG_CLIENT_IP < LIBRPZ_TRIG_QNAME < LIBRPZ_TRIG_IP
+ * < LIBRPZ_TRIG_NSDNAME < LIBRPZ_TRIG_NSIP}
+ */
+typedef enum {
+ LIBRPZ_TRIG_BAD =0,
+ LIBRPZ_TRIG_CLIENT_IP =1,
+ LIBRPZ_TRIG_QNAME =2,
+ LIBRPZ_TRIG_IP =3,
+ LIBRPZ_TRIG_NSDNAME =4,
+ LIBRPZ_TRIG_NSIP =5
+} librpz_trig_t;
+#define LIBRPZ_TRIG_SIZE 3 /* sizeof librpz_trig_t in bits */
+typedef uint8_t librpz_tbit_t; /* one bit for each of the TRIGS_NUM
+ * trigger types */
+
+
+/*
+ * Response Policy Zone Actions or policies
+ */
+typedef enum {
+ LIBRPZ_POLICY_UNDEFINED =0, /* an empty entry or no decision yet */
+ LIBRPZ_POLICY_DELETED =1, /* placeholder for a deleted policy */
+
+ LIBRPZ_POLICY_PASSTHRU =2, /* 'passthru': do not rewrite */
+ LIBRPZ_POLICY_DROP =3, /* 'drop': do not respond */
+ LIBRPZ_POLICY_TCP_ONLY =4, /* 'tcp-only': answer UDP with TC=1 */
+ LIBRPZ_POLICY_NXDOMAIN =5, /* 'nxdomain': answer with NXDOMAIN */
+ LIBRPZ_POLICY_NODATA =6, /* 'nodata': answer with ANCOUNT=0 */
+ LIBRPZ_POLICY_RECORD =7, /* rewrite with the policy's RR */
+
+ /* only in client configurations to override the zone */
+ LIBRPZ_POLICY_GIVEN, /* 'given': what policy record says */
+ LIBRPZ_POLICY_DISABLED, /* at most log */
+ LIBRPZ_POLICY_CNAME, /* answer with 'cname x' */
+} librpz_policy_t;
+#define LIBRPZ_POLICY_BITS 4
+
+/*
+ * Special policies that appear as targets of CNAMEs
+ * NXDOMAIN is signaled by a CNAME with a "." target.
+ * NODATA is signaled by a CNAME with a "*." target.
+ */
+#define LIBRPZ_RPZ_PREFIX "rpz-"
+#define LIBRPZ_RPZ_PASSTHRU LIBRPZ_RPZ_PREFIX"passthru"
+#define LIBRPZ_RPZ_DROP LIBRPZ_RPZ_PREFIX"drop"
+#define LIBRPZ_RPZ_TCP_ONLY LIBRPZ_RPZ_PREFIX"tcp-only"
+
+
+typedef uint16_t librpz_dznum_t; /* dnsrpzd zone # in [0,DZNUM_MAX] */
+typedef uint8_t librpz_cznum_t; /* client zone # in [0,CZNUM_MAX] */
+
+
+/*
+ * CIDR block
+ */
+typedef struct librpz_prefix {
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } addr;
+ uint8_t family;
+ uint8_t len;
+} librpz_prefix_t;
+
+/*
+ * A domain
+ */
+typedef uint8_t librpz_dsize_t;
+typedef struct librpz_domain {
+ librpz_dsize_t size; /* of only .d */
+ uint8_t d[0]; /* variable length wire format */
+} librpz_domain_t;
+
+/*
+ * A maximal domain buffer
+ */
+typedef struct librpz_domain_buf {
+ librpz_dsize_t size;
+ uint8_t d[NS_MAXCDNAME];
+} librpz_domain_buf_t;
+
+/*
+ * A resource record without the owner name.
+ * C compilers say that sizeof(librpz_rr_t)=12 instead of 10.
+ */
+typedef struct {
+ uint16_t type; /* network byte order */
+ uint16_t class; /* network byte order */
+ uint32_t ttl; /* network byte order */
+ uint16_t rdlength; /* network byte order */
+ uint8_t rdata[0]; /* variable length */
+} librpz_rr_t;
+
+/*
+ * The database file might be mapped with different starting addresses
+ * by concurrent clients (resolvers), and so all pointers are offsets.
+ */
+typedef uint32_t librpz_idx_t;
+#define LIBRPZ_IDX_NULL 0
+#define LIBRPZ_IDX_MIN 1
+#define LIBRPZ_IDX_BAD ((librpz_idx_t)-1)
+/**
+ * Partial decoded results of a set of RPZ queries for a single DNS response
+ * or iteration through the mapped file.
+ */
+typedef int16_t librpz_result_id_t;
+typedef struct librpz_result {
+ librpz_idx_t next_rr;
+ librpz_result_id_t hit_id; /* trigger ID from resolver */
+ librpz_policy_t zpolicy; /* policy from zone */
+ librpz_policy_t policy; /* adjusted by client configuration */
+ librpz_dznum_t dznum; /* dnsrpzd zone number */
+ librpz_cznum_t cznum; /* librpz client zone number */
+ librpz_trig_t trig:LIBRPZ_TRIG_SIZE;
+ bool log:1; /* log rewrite given librpz_log_level */
+} librpz_result_t;
+
+
+/**
+ * librpz trace or log levels.
+ */
+typedef enum {
+ LIBRPZ_LOG_FATAL =0, /* always print fatal errors */
+ LIBRPZ_LOG_ERROR =1, /* errors have this level */
+ LIBRPZ_LOG_TRACE1 =2, /* big events such as dnsrpzd starts */
+ LIBRPZ_LOG_TRACE2 =3, /* smaller dnsrpzd zone transfers */
+ LIBRPZ_LOG_TRACE3 =4, /* librpz hits */
+ LIBRPZ_LOG_TRACE4 =5, /* librpz lookups */
+ LIBRPZ_LOG_INVALID =999,
+} librpz_log_level_t;
+typedef librpz_log_level_t (librpz_log_level_val_t)(librpz_log_level_t level);
+LIBDEF_F(log_level_val)
+
+/**
+ * Logging function that can be supplied by the resolver.
+ * @param level is one of librpz_log_level_t
+ * @param ctx is for use by the resolver's logging system.
+ * NULL mean a context-free message.
+ */
+typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx,
+ const char *buf);
+
+/**
+ * Point librpz logging functions to the resolver's choice.
+ */
+typedef void (librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm);
+LIBDEF_F(set_log)
+
+
+/**
+ * librpz error messages are put in these buffers.
+ * Use a structure instead of naked char* to let the compiler check the length.
+ * A function defined with "foo(char buf[120])" can be called with
+ * "char sbuf[2]; foo(sbuf)" and suffer a buffer overrun.
+ */
+typedef struct {
+ char c[120];
+} librpz_emsg_t;
+
+
+#ifdef LIBRPZ_HAVE_ATTR
+#define LIBRPZ_UNUSED __attribute__((unused))
+#define LIBRPZ_PF(f,l) __attribute__((format(printf,f,l)))
+#define LIBRPZ_NORET __attribute__((__noreturn__))
+#else
+#define LIBRPZ_UNUSED
+#define LIBRPZ_PF(f,l)
+#define LIBRPZ_NORET
+#endif
+
+#ifdef HAVE_BUILTIN_EXPECT
+#define LIBRPZ_LIKELY(c) __builtin_expect(!!(c), 1)
+#define LIBRPZ_UNLIKELY(c) __builtin_expect(!!(c), 0)
+#else
+#define LIBRPZ_LIKELY(c) (c)
+#define LIBRPZ_UNLIKELY(c) (c)
+#endif
+
+typedef bool (librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg);
+LIBDEF_F(parse_log_opt)
+
+typedef void (librpz_vpemsg_t)(librpz_emsg_t *emsg,
+ const char *p, va_list args);
+LIBDEF_F(vpemsg)
+typedef void (librpz_pemsg_t)(librpz_emsg_t *emsg,
+ const char *p, ...) LIBRPZ_PF(2,3);
+LIBDEF_F(pemsg)
+
+typedef void (librpz_vlog_t)(librpz_log_level_t level, void *ctx,
+ const char *p, va_list args);
+LIBDEF_F(vlog)
+typedef void (librpz_log_t)(librpz_log_level_t level, void *ctx,
+ const char *p, ...) LIBRPZ_PF(3,4);
+LIBDEF_F(log)
+
+typedef void (librpz_fatal_t)(int ex_code,
+ const char *p, ...) LIBRPZ_PF(2,3);
+extern void librpz_fatal(int ex_code,
+ const char *p, ...) LIBRPZ_PF(2,3) LIBRPZ_NORET;
+
+typedef void (librpz_rpz_assert_t)(const char *file, unsigned line,
+ const char *p, ...) LIBRPZ_PF(3,4);
+extern void librpz_rpz_assert(const char *file, unsigned line,
+ const char *p, ...) LIBRPZ_PF(3,4) LIBRPZ_NORET;
+
+typedef void (librpz_rpz_vassert_t)(const char *file, uint line,
+ const char *p, va_list args);
+extern void librpz_rpz_vassert(const char *file, uint line,
+ const char *p, va_list args) LIBRPZ_NORET;
+
+
+/*
+ * As far as clients are concerned, all relative pointers or indexes in a
+ * version of the mapped file except trie node parent pointers remain valid
+ * forever. A client must release a version so that it can be garbage
+ * collected by the file system. When dnsrpzd needs to expand the file,
+ * it copies the old file to a new, larger file. Clients can continue
+ * using the old file.
+ *
+ * Versions can also appear in a single file. Old nodes and trie values
+ * within the file are not destroyed until all clients using the version
+ * that contained the old values release the version.
+ *
+ * A client is marked as using version by connecting to the daemon. It is
+ * marked as using all subsequent versions. A client releases all versions
+ * by closing the connection or a range of versions by updating is slot
+ * in the shared memory version table.
+ *
+ * As far as clients are concerned, there are the following possible librpz
+ * failures:
+ * - malloc() or other fatal internal librpz problems indicated by
+ * a failing return from a librpz function
+ * All operations will fail until client handle is destroyed and
+ * recreated with librpz_client_detach() and librpz_client_create().
+ * - corrupt database detected by librpz code, corrupt database detected
+ * by dnsrpzd, or disconnection from the daemon.
+ * Current operations will fail.
+ *
+ * Clients assume that the file has already been unlinked before
+ * the corrupt flag is set so that they do not race with the server
+ * over the corruption of a single file. A client that finds the
+ * corrupt set knows that dnsrpzd has already crashed with
+ * abort() and is restarting. The client can re-connect to dnsrpzd
+ * and retransmit its configuration, backing off as usual if anything
+ * goes wrong.
+ *
+ * Searches of the database by a client do not need locks against dnsrpzd or
+ * other clients, but a lock is used to protect changes to the connection
+ * by competing threads in the client. The client provides functions
+ * to serialize the concurrent use of any single client handle.
+ * Functions that do nothing are appropriate for applications that are
+ * not "threaded" or that do not share client handles among threads.
+ * Otherwise, functions must be provided to librpz_clientcreate().
+ * Something like the following works with pthreads:
+ *
+ * static void
+ * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); }
+ *
+ * static void
+ * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); }
+ *
+ * static void
+ * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); }
+ *
+ *
+ *
+ * At every instant, all of the data and pointers in the mapped file are valid.
+ * Changes to trie node or other data are always made so that it and
+ * all pointers in and to it remain valid for a time. Old versions are
+ * eventually discarded.
+ *
+ * Dnsrpzd periodically defines a new version by setting aside all changes
+ * made since the previous version was defined. Subsequent changes
+ * made (only!) by dnsrpzd will be part of the next version.
+ *
+ * To discard an old version, dnsrpzd must know that all clients have stopped
+ * using that version. Clients do that by using part of the mapped file
+ * to tell dnsrpzd the oldest version that each client is using.
+ * Dnsrpzd assigns each connecting client an entry in the cversions array
+ * in the mapped file. The client puts version numbers into that entry
+ * to signal to dnsrpzd which versions that can be discarded.
+ * Dnsrpzd is free, as far as that client is concerned, to discard all
+ * numerically smaller versions. A client can disclaim all versions with
+ * the version number VERSIONS_ALL or 0.
+ *
+ * The race between a client changing its entry and dnsrpzd discarding a
+ * version is resolved by allowing dnsrpzd to discard all versions
+ * smaller or equal to the client's version number. If dnsrpzd is in
+ * the midst of discarding or about to discard version N when the
+ * client asserts N, no harm is done. The client depends only on
+ * the consistency of version N+1.
+ *
+ * This version mechanism depends in part on not being exercised too frequently
+ * Version numbers are 32 bits long and dnsrpzd creates new versions
+ * at most once every 30 seconds.
+ */
+
+
+/*
+ * Lock functions for concurrent use of a single librpz_client_t client handle.
+ */
+typedef void(librpz_mutex_t)(void *mutex);
+
+/*
+ * List of connections to dnsrpzd daemons.
+ */
+typedef struct librpz_clist librpz_clist_t;
+
+/*
+ * Client's handle on dnsrpzd.
+ */
+typedef struct librpz_client librpz_client_t;
+
+/**
+ * Create the list of connections to the dnsrpzd daemon.
+ * @param[out] emsg: error message
+ * @param lock: start exclusive access to the client handle
+ * @param unlock: end exclusive access to the client handle
+ * @param mutex_destroy: release the lock
+ * @param mutex: pointer to the lock for the client handle
+ * @param log_ctx: NULL or resolver's context log messages
+ */
+typedef librpz_clist_t *(librpz_clist_create_t)(librpz_emsg_t *emsg,
+ librpz_mutex_t *lock,
+ librpz_mutex_t *unlock,
+ librpz_mutex_t *mutex_destroy,
+ void *mutex, void *log_ctx);
+LIBDEF_F(clist_create)
+
+
+/**
+ * Release the list of dnsrpzd connections.
+ */
+typedef void (librpz_clist_detach_t)(librpz_clist_t **clistp);
+LIBDEF_F(clist_detach)
+
+/**
+ * Create a librpz client handle.
+ * @param[out] emsg: error message
+ * @param: list of dnsrpzd connections
+ * @param cstr: string of configuration settings separated by ';' or '\n'
+ * @param use_expired: true to not ignore expired zones
+ * @return client handle or NULL if the handle could not be created
+ */
+typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t *emsg,
+ librpz_clist_t *clist,
+ const char *cstr,
+ bool use_expired);
+LIBDEF_F(client_create)
+
+/**
+ * Start (if necessary) dnsrpzd and connect to it.
+ * @param[out] emsg: error message
+ * @param client handle
+ * @param optional: true if it is ok if starting the daemon is not allowed
+ */
+typedef bool (librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client,
+ bool optional);
+LIBDEF_F(connect)
+
+/**
+ * Start to destroy a librpz client handle.
+ * It will not be destroyed until the last set of RPZ queries represented
+ * by a librpz_rsp_t ends.
+ * @param client handle to be released
+ * @return false on error
+ */
+typedef void (librpz_client_detach_t)(librpz_client_t **clientp);
+LIBDEF_F(client_detach)
+
+/**
+ * State for a set of RPZ queries for a single DNS response
+ * or for listing the database.
+ */
+typedef struct librpz_rsp librpz_rsp_t;
+
+/**
+ * Start a set of RPZ queries for a single DNS response.
+ * @param[out] emsg: error message for false return or *rspp=NULL
+ * @param[out] rspp created context or NULL
+ * @param[out] min_ns_dotsp: NULL or pointer to configured MIN-NS-DOTS value
+ * @param client state
+ * @param have_rd: RD=1 in the DNS request
+ * @param have_do: DO=1 in the DNS request
+ * @return false on error
+ */
+typedef bool (librpz_rsp_create_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
+ int *min_ns_dotsp, librpz_client_t *client,
+ bool have_rd, bool have_do);
+LIBDEF_F(rsp_create)
+
+/**
+ * Finish RPZ work for a DNS response.
+ */
+typedef void (librpz_rsp_detach_t)(librpz_rsp_t **rspp);
+LIBDEF_F(rsp_detach)
+
+/**
+ * Get the final, accumulated result of a set of RPZ queries.
+ * Yield LIBRPZ_POLICY_UNDEFINED if
+ * - there were no hits,
+ * - there was a dispositive hit, be we have not recursed and are required
+ * to recurse so that evil DNS authorities will not know we are using RPZ
+ * - we have a hit and have recursed, but later data such as NSIP could
+ * override
+ * @param[out] emsg
+ * @param[out] result describes the hit
+ * or result->policy=LIBRPZ_POLICY_UNDEFINED without a hit
+ * @param[out] result: current policy rewrite values
+ * @param recursed: recursion has now been done even if it was not done
+ * when the hit was found
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_result_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+ bool recursed, const librpz_rsp_t *rsp);
+LIBDEF_F(rsp_result)
+
+/**
+ * Might looking for a trigger be worthwhile?
+ * @param trig: look for this type of trigger
+ * @param ipv6: true if trig is LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP,
+ * or LIBRPZ_TRIG_NSIP and the IP address is IPv6
+ * @return: true if looking could be worthwhile
+ */
+typedef bool (librpz_have_trig_t)(librpz_trig_t trig, bool ipv6,
+ const librpz_rsp_t *rsp);
+LIBDEF_F(have_trig)
+
+/**
+ * Might looking for NSDNAME and NSIP triggers be worthwhile?
+ * @return: true if looking could be worthwhile
+ */
+typedef bool (librpz_have_ns_trig_t)(const librpz_rsp_t *rsp);
+LIBDEF_F(have_ns_trig)
+
+/**
+ * Convert the found client IP trie key to a CIDR block
+ * @param[out] emsg
+ * @param[out] prefix trigger
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_clientip_prefix_t)(librpz_emsg_t *emsg,
+ librpz_prefix_t *prefix,
+ librpz_rsp_t *rsp);
+LIBDEF_F(rsp_clientip_prefix)
+
+/**
+ * Compute the owner name of the found or result trie key, usually to log it.
+ * An IP address key might be returned as 8.0.0.0.127.rpz-client-ip.
+ * example.com. might be a qname trigger. example.com.rpz-nsdname. could
+ * be an NSDNAME trigger.
+ * @param[out] emsg
+ * @param[out] owner domain
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_domain_t)(librpz_emsg_t *emsg,
+ librpz_domain_buf_t *owner,
+ librpz_rsp_t *rsp);
+LIBDEF_F(rsp_domain)
+
+/**
+ * Get the next RR of the LIBRPZ_POLICY_RECORD result after an initial use of
+ * librpz_rsp_result() or librpz_itr_node() or after a previous use of
+ * librpz_rsp_rr(). The RR is in uncompressed wire format including type,
+ * class, ttl and length in network byte order.
+ * @param[out] emsg
+ * @param[out] typep: optional host byte order record type or ns_t_invalid (0)
+ * @param[out] classp: class such as ns_c_in
+ * @param[out] ttlp: TTL
+ * @param[out] rrp: optional malloc() buffer containing the next RR or
+ * NULL after the last RR
+ * @param[out] result: current policy rewrite values
+ * @param qname: used construct a wildcard CNAME
+ * @param qname_size
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_rr_t)(librpz_emsg_t *emsg, uint16_t *typep,
+ uint16_t *classp, uint32_t *ttlp,
+ librpz_rr_t **rrp, librpz_result_t *result,
+ const uint8_t *qname, size_t qname_size,
+ librpz_rsp_t *rsp);
+LIBDEF_F(rsp_rr)
+
+/**
+ * Get the next RR of the LIBRPZ_POLICY_RECORD result.
+ * @param[out] emsg
+ * @param[out] ttlp: TTL
+ * @param[out] rrp: malloc() buffer with SOA RR without owner name
+ * @param[out] result: current policy rewrite values
+ * @param[out] origin: SOA owner name
+ * @param[out] origin_size
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_soa_t)(librpz_emsg_t *emsg, uint32_t *ttlp,
+ librpz_rr_t **rrp, librpz_domain_buf_t *origin,
+ librpz_result_t *result, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_soa)
+
+/**
+ * Get the SOA serial number for a policy zone to compare with a known value
+ * to check whether a zone transfer is complete.
+ */
+typedef bool (librpz_soa_serial_t)(librpz_emsg_t *emsg, uint32_t *serialp,
+ const char *domain_nm, librpz_rsp_t *rsp);
+LIBDEF_F(soa_serial)
+
+/**
+ * Save the current policy checking state.
+ * @param[out] emsg
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_push)
+#define LIBRPZ_RSP_STACK_DEPTH 3
+
+/**
+ * Restore the previous policy checking state.
+ * @param[out] emsg
+ * @param[out] result: NULL or restored policy rewrite values
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+ librpz_rsp_t *rsp);
+LIBDEF_F(rsp_pop)
+
+/**
+ * Discard the most recently save policy checking state.
+ * @param[out] emsg
+ * @param[out] result: NULL or restored policy rewrite values
+ * @return false on error
+ */
+typedef bool (librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_pop_discard)
+
+/**
+ * Disable a zone.
+ * @param[out] emsg
+ * @param znum
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_rsp_forget_zone_t)(librpz_emsg_t *emsg,
+ librpz_cznum_t znum, librpz_rsp_t *rsp);
+LIBDEF_F(rsp_forget_zone)
+
+/**
+ * Apply RPZ to an IP address.
+ * @param[out] emsg
+ * @param addr: address to check
+ * @param ipv6: true for 16 byte IPv6 instead of 4 byte IPv4
+ * @param trig LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, or LIBRPZ_TRIG_NSIP
+ * @param hit_id: caller chosen
+ * @param recursed: recursion has been done
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_ck_ip_t)(librpz_emsg_t *emsg,
+ const void *addr, uint family,
+ librpz_trig_t trig, librpz_result_id_t hit_id,
+ bool recursed, librpz_rsp_t *rsp);
+LIBDEF_F(ck_ip)
+
+/**
+ * Apply RPZ to a wire-format domain.
+ * @param[out] emsg
+ * @param domain in wire format
+ * @param domain_size
+ * @param trig LIBRPZ_TRIG_QNAME or LIBRPZ_TRIG_NSDNAME
+ * @param hit_id: caller chosen
+ * @param recursed: recursion has been done
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return false on error
+ */
+typedef bool (librpz_ck_domain_t)(librpz_emsg_t *emsg,
+ const uint8_t *domain, size_t domain_size,
+ librpz_trig_t trig, librpz_result_id_t hit_id,
+ bool recursed, librpz_rsp_t *rsp);
+LIBDEF_F(ck_domain)
+
+/**
+ * Ask dnsrpzd to refresh a zone.
+ * @param[out] emsg error message
+ * @param librpz_domain_t domain to refresh
+ * @param client context
+ * @return false after error
+ */
+typedef bool (librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain,
+ librpz_rsp_t *rsp);
+LIBDEF_F(zone_refresh)
+
+/**
+ * Get a string describing the database
+ * @param license: include the license
+ * @param cfiles: include the configuration file names
+ * @param listens: include the local notify IP addresses
+ * @param[out] emsg error message if the result is null
+ * @param client context
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg,
+ bool license, bool cfiles, bool listens,
+ librpz_rsp_t *rsp);
+LIBDEF_F(db_info)
+
+/**
+ * Start a context for listing the nodes and/or zones in the mapped file
+ * @param[out] emsg: error message for false return or *rspp=NULL
+ * @param[out[ rspp created context or NULL
+ * @param client context
+ * @return false after error
+ */
+typedef bool (librpz_itr_start_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp,
+ librpz_client_t *client);
+LIBDEF_F(itr_start)
+
+/**
+ * Get mapped file memory allocation statistics.
+ * @param[out] emsg: error message
+ * @param rsp state from librpz_itr_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(mf_stats)
+
+/**
+ * Get versions currently used by clients.
+ * @param[out] emsg: error message
+ * @param[in,out] rsp: state from librpz_itr_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp);
+LIBDEF_F(vers_stats)
+
+/**
+ * Allocate a string describing the next zone or "" after the last zone.
+ * @param[out] emsg
+ * @param all_zones to list all instead of only requested zones
+ * @param[in,out] rsp state from librpz_rsp_start()
+ * @return malloc'ed string or NULL after error
+ */
+typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones,
+ librpz_rsp_t *rsp);
+LIBDEF_F(itr_zone)
+
+/**
+ * Describe the next trie node while dumping the database.
+ * @param[out] emsg
+ * @param[out] result describes node
+ * or result->policy=LIBRPZ_POLICY_UNDEFINED after the last node.
+ * @param all_zones to list all instead of only requested zones
+ * @param[in,out] rsp state from librpz_itr_start()
+ * @return: false on error
+ */
+typedef bool (librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result,
+ bool all_zones, librpz_rsp_t *rsp);
+LIBDEF_F(itr_node)
+
+/**
+ * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size
+ */
+typedef const char *(librpz_policy2str_t)(librpz_policy_t policy,
+ char *buf, size_t buf_size);
+#define POLICY2STR_SIZE sizeof("policy xxxxxx")
+LIBDEF_F(policy2str)
+
+/**
+ * Trigger type to string.
+ */
+typedef const char *(librpz_trig2str_t)(librpz_trig_t trig);
+LIBDEF_F(trig2str)
+
+/**
+ * Convert a number of seconds to a zone file duration string
+ */
+typedef const char *(librpz_secs2str_t)(time_t secs,
+ char *buf, size_t buf_size);
+#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s")
+LIBDEF_F(secs2str)
+
+/**
+ * Parse a duration with 's', 'm', 'h', 'd', and 'w' units.
+ */
+typedef bool (librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val,
+ const char *str0);
+LIBDEF_F(str2secs)
+
+/**
+ * Translate selected rtypes to strings
+ */
+typedef const char *(librpz_rtype2str_t)(uint type, char *buf, size_t buf_size);
+#define RTYPE2STR_SIZE sizeof("type xxxxx")
+LIBDEF_F(rtype2str)
+
+/**
+ * Local version of ns_name_ntop() for portability.
+ */
+typedef int (librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz);
+LIBDEF_F(domain_ntop)
+
+/**
+ * Local version of ns_name_pton().
+ */
+typedef int (librpz_domain_pton2_t)(const char *src, u_char *dst, size_t dstsiz,
+ size_t *dstlen, bool lower);
+LIBDEF_F(domain_pton2)
+
+typedef union socku socku_t;
+typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp,
+ in_port_t port);
+LIBDEF_F(mk_inet_su)
+
+typedef socku_t *(librpz_mk_inet6_su_t)(socku_t *su, const
+ struct in6_addr *addrp,
+ uint32_t scope_id, in_port_t port);
+LIBDEF_F(mk_inet6_su)
+
+typedef bool (librpz_str2su_t)(socku_t *sup, const char *str);
+LIBDEF_F(str2su)
+
+typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su);
+LIBDEF_F(su2str)
+#define SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
+
+
+/**
+ * default path to dnsrpzd
+ */
+const char *librpz_dnsrpzd_path;
+
+
+#undef LIBDEF
+
+/*
+ * This is the dlopen() interface to librpz.
+ */
+typedef const struct {
+ const char *dnsrpzd_path;
+ const char *version;
+ librpz_parse_log_opt_t *parse_log_opt;
+ librpz_log_level_val_t *log_level_val;
+ librpz_set_log_t *set_log;
+ librpz_vpemsg_t *vpemsg;
+ librpz_pemsg_t *pemsg;
+ librpz_vlog_t *vlog;
+ librpz_log_t *log;
+ librpz_fatal_t *fatal LIBRPZ_NORET;
+ librpz_rpz_assert_t *rpz_assert LIBRPZ_NORET;
+ librpz_rpz_vassert_t *rpz_vassert LIBRPZ_NORET;
+ librpz_clist_create_t *clist_create;
+ librpz_clist_detach_t *clist_detach;
+ librpz_client_create_t *client_create;
+ librpz_connect_t *connect;
+ librpz_client_detach_t *client_detach;
+ librpz_rsp_create_t *rsp_create;
+ librpz_rsp_detach_t *rsp_detach;
+ librpz_rsp_result_t *rsp_result;
+ librpz_have_trig_t *have_trig;
+ librpz_have_ns_trig_t *have_ns_trig;
+ librpz_rsp_clientip_prefix_t *rsp_clientip_prefix;
+ librpz_rsp_domain_t *rsp_domain;
+ librpz_rsp_rr_t *rsp_rr;
+ librpz_rsp_soa_t *rsp_soa;
+ librpz_soa_serial_t *soa_serial;
+ librpz_rsp_push_t *rsp_push;
+ librpz_rsp_pop_t *rsp_pop;
+ librpz_rsp_pop_discard_t *rsp_pop_discard;
+ librpz_rsp_forget_zone_t *rsp_forget_zone;
+ librpz_ck_ip_t *ck_ip;
+ librpz_ck_domain_t *ck_domain;
+ librpz_zone_refresh_t *zone_refresh;
+ librpz_db_info_t *db_info;
+ librpz_itr_start_t *itr_start;
+ librpz_mf_stats_t *mf_stats;
+ librpz_vers_stats_t *vers_stats;
+ librpz_itr_zone_t *itr_zone;
+ librpz_itr_node_t *itr_node;
+ librpz_policy2str_t *policy2str;
+ librpz_trig2str_t *trig2str;
+ librpz_secs2str_t *secs2str;
+ librpz_str2secs_t *str2secs;
+ librpz_rtype2str_t *rtype2str;
+ librpz_domain_ntop_t *domain_ntop;
+ librpz_domain_pton2_t *domain_pton2;
+ librpz_mk_inet_su_t *mk_inet_su;
+ librpz_mk_inet6_su_t *mk_inet6_su;
+ librpz_str2su_t *str2su;
+ librpz_su2str_t *su2str;
+} librpz_0_t;
+extern librpz_0_t librpz_def_0;
+
+/*
+ * Future versions can be upward compatible by defining LIBRPZ_DEF as
+ * librpz_X_t.
+ */
+#define LIBRPZ_DEF librpz_def_0
+#define LIBRPZ_DEF_STR "librpz_def_0"
+
+typedef librpz_0_t librpz_t;
+extern librpz_t *librpz;
+
+
+#if LIBRPZ_LIB_OPEN == 2
+#include <dlfcn.h>
+
+/**
+ * link-load librpz
+ * @param[out] emsg: error message
+ * @param[in,out] dl_handle: NULL or pointer to new dlopen handle
+ * @param[in] path: librpz.so path
+ * @return address of interface structure or NULL on failure
+ */
+static inline librpz_t *
+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
+{
+ void *handle;
+ librpz_t *new_librpz;
+
+ emsg->c[0] = '\0';
+
+ /*
+ * Close a previously opened handle on librpz.so.
+ */
+ if (dl_handle != NULL && *dl_handle != NULL) {
+ if (dlclose(*dl_handle) != 0) {
+ snprintf(emsg->c, sizeof(librpz_emsg_t),
+ "dlopen(NULL): %s", dlerror());
+ return (NULL);
+ }
+ *dl_handle = NULL;
+ }
+
+ /*
+ * First try the main executable of the process in case it was
+ * linked to librpz.
+ * Do not worry if we cannot search the main executable of the process.
+ */
+ handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL);
+ if (handle != NULL) {
+ new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
+ if (new_librpz != NULL) {
+ if (dl_handle != NULL)
+ *dl_handle = handle;
+ return (new_librpz);
+ }
+ if (dlclose(handle) != 0) {
+ snprintf(emsg->c, sizeof(librpz_emsg_t),
+ "dlsym(NULL, "LIBRPZ_DEF_STR"): %s",
+ dlerror());
+ return (NULL);
+ }
+ }
+
+ if (path == NULL || path[0] == '\0') {
+ snprintf(emsg->c, sizeof(librpz_emsg_t),
+ "librpz not linked and no dlopen() path provided");
+ return (NULL);
+ }
+
+ handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ if (handle == NULL) {
+ snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s",
+ path, dlerror());
+ return (NULL);
+ }
+ new_librpz = dlsym(handle, LIBRPZ_DEF_STR);
+ if (new_librpz != NULL) {
+ if (dl_handle != NULL)
+ *dl_handle = handle;
+ return (new_librpz);
+ }
+ snprintf(emsg->c, sizeof(librpz_emsg_t),
+ "dlsym(%s, "LIBRPZ_DEF_STR"): %s",
+ path, dlerror());
+ dlclose(handle);
+ return (NULL);
+}
+#elif defined(LIBRPZ_LIB_OPEN)
+/*
+ * Statically link to the librpz.so DSO on systems without dlopen()
+ */
+static inline librpz_t *
+librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path)
+{
+ (void)(path);
+
+ if (dl_handle != NULL)
+ *dl_handle = NULL;
+
+#if LIBRPZ_LIB_OPEN == 1
+ emsg->c[0] = '\0';
+ return (&LIBRPZ_DEF);
+#else
+ snprintf(emsg->c, sizeof(librpz_emsg_t),
+ "librpz not available via ./configure");
+ return (NULL);
+#endif /* LIBRPZ_LIB_OPEN */
+}
+#endif /* LIBRPZ_LIB_OPEN */
+
+#endif /* LIBRPZ_H */
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-/* $Id$ */
-
#ifndef DNS_RPZ_H
#define DNS_RPZ_H 1
struct dns_rpz_popt {
dns_rpz_zbits_t no_rd_ok;
dns_rpz_zbits_t no_log;
+ dns_rpz_zbits_t nsip_on;
+ dns_rpz_zbits_t nsdname_on;
+ isc_boolean_t dnsrps_enabled;
isc_boolean_t break_dnssec;
isc_boolean_t qname_wait_recurse;
isc_boolean_t nsip_wait_recurse;
dns_rpz_triggers_t triggers[DNS_RPZ_MAX_ZONES];
/*
- * RPZ policy version number (initially 0, increases whenever
- * the server is reconfigured with new zones or policy)
+ * RPZ policy version number.
+ * It is initially 0 and it increases whenever the server is
+ * reconfigured with new zones or policy.
*/
int rpz_ver;
dns_rpz_cidr_node_t *cidr;
dns_rbt_t *rbt;
+
+ /*
+ * DNSRPZ librpz configuration string and handle on librpz connection
+ */
+ char *rps_cstr;
+ size_t rps_cstr_size;
+ struct librpz_client *rps_client;
};
dns_rpz_popt_t popt;
int rpz_ver;
+ /*
+ * Shim db between BIND and DNRPS librpz.
+ */
+ dns_db_t *rpsdb;
+
/*
* p_name: current policy owner name
* r_name: recursing for this name to possible policy triggers
dns_name_t *selfname);
isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, char *rps_cstr,
+ size_t rps_cstr_size, isc_mem_t *mctx,
isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
isc_result_t
#include <dns/db.h>
#include <dns/dbiterator.h>
+#include <dns/dnsrps.h>
#include <dns/events.h>
#include <dns/fixedname.h>
#include <dns/log.h>
* qname_wait_recurse and qname_skip_recurse are used to
* implement the "qname-wait-recurse" config option.
*
- * By default, "qname-wait-recurse" is yes, so no
- * processing happens without recursion. In this case,
- * qname_wait_recurse is true, and qname_skip_recurse
- * (a bit field indicating which policy zones can be
- * processed without recursion) is set to all 0's by
+ * When "qname-wait-recurse" is yes, no processing happens without
+ * recursion. In this case, qname_wait_recurse is true, and
+ * qname_skip_recurse (a bit field indicating which policy zones
+ * can be processed without recursion) is set to all 0's by
* fix_qname_skip_recurse().
*
* When "qname-wait-recurse" is no, qname_skip_recurse may be
* Convert an IP address from radix tree binary (host byte order) to
* to its canonical response policy domain name without the origin of the
* policy zone.
+ *
+ * Generate a name for an IPv6 address that fits RFC 5952, except that our
+ * reversed format requires that when the length of the consecutive 16-bit
+ * 0 fields are equal (e.g., 1.0.0.1.0.0.db8.2001 corresponding to
+ * 2001:db8:0:0:1:0:0:1), we shorted the last instead of the first
+ * (e.g., 1.0.0.1.zz.db8.2001 corresponding to 2001:db8::1:0:0:1).
*/
static isc_result_t
ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix,
char str[1+8+1+INET6_ADDRSTRLEN+1];
isc_buffer_t buffer;
isc_result_t result;
- isc_boolean_t zeros;
+ int best_first, best_len, cur_first, cur_len;
int i, n, len;
if (KEY_IS_IPV4(tgt_prefix, tgt_ip)) {
(tgt_ip->w[3]>>8) & 0xff,
(tgt_ip->w[3]>>16) & 0xff,
(tgt_ip->w[3]>>24) & 0xff);
- if (len < 0 || len > (int)sizeof(str))
+ if (len < 0 || len > (int)sizeof(str)) {
return (ISC_R_FAILURE);
+ }
} else {
+ len = snprintf(str, sizeof(str), "%d", tgt_prefix);
+ if (len == -1) {
+ return (ISC_R_FAILURE);
+ }
+
for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16)
& 0xffff);
w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff;
}
- zeros = ISC_FALSE;
- len = snprintf(str, sizeof(str), "%d", tgt_prefix);
- if (len == -1)
- 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) {
- INSIST((size_t)len <= sizeof(str));
- n = snprintf(&str[len], sizeof(str) - len,
- ".%x", w[i++]);
- if (n < 0)
- return (ISC_R_FAILURE);
- len += n;
+ /*
+ * Find the start and length of the first longest sequence
+ * of zeros in the address.
+ */
+ best_first = -1;
+ best_len = 0;
+ cur_first = -1;
+ cur_len = 0;
+ for (n = 0; n <=7; ++n) {
+ if (w[n] != 0) {
+ cur_len = 0;
+ cur_first = -1;
} else {
- zeros = ISC_TRUE;
- INSIST((size_t)len <= sizeof(str));
- n = snprintf(&str[len], sizeof(str) - len,
- ".zz");
- if (n < 0)
- return (ISC_R_FAILURE);
- len += n;
- i += 2;
- while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0)
- ++i;
+ ++cur_len;
+ if (cur_first < 0) {
+ cur_first = n;
+ } else if (cur_len >= best_len) {
+ best_first = cur_first;
+ best_len = cur_len;
+ }
+ }
+ }
+
+ for (n = 0; n <= 7; ++n) {
+ INSIST(len < (int)sizeof(str));
+ if (n == best_first) {
+ len += snprintf(str + len, sizeof(str) - len,
+ ".zz");
+ n += best_len - 1;
+ } else {
+ len += snprintf(str + len, sizeof(str) - len,
+ ".%x", w[n]);
}
- if (len >= (int)sizeof(str))
- return (ISC_R_FAILURE);
}
}
}
/*
- * Determine the type a of a name in a response policy zone.
+ * Determine the type of a name in a response policy zone.
*/
static dns_rpz_type_t
-type_from_name(dns_rpz_zone_t *rpz, const dns_name_t *name) {
-
- if (dns_name_issubdomain(name, &rpz->ip))
+type_from_name(const dns_rpz_zones_t *rpzs,
+ dns_rpz_zone_t *rpz, const dns_name_t *name)
+{
+ if (dns_name_issubdomain(name, &rpz->ip)) {
return (DNS_RPZ_TYPE_IP);
+ }
- if (dns_name_issubdomain(name, &rpz->client_ip))
+ if (dns_name_issubdomain(name, &rpz->client_ip)) {
return (DNS_RPZ_TYPE_CLIENT_IP);
+ }
-#ifdef ENABLE_RPZ_NSIP
- if (dns_name_issubdomain(name, &rpz->nsip))
+ if ((rpzs->p.nsip_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
+ dns_name_issubdomain(name, &rpz->nsip))
+ {
return (DNS_RPZ_TYPE_NSIP);
-#endif
+ }
-#ifdef ENABLE_RPZ_NSDNAME
- if (dns_name_issubdomain(name, &rpz->nsdname))
+ if ((rpzs->p.nsdname_on & DNS_RPZ_ZBIT(rpz->num)) != 0 &&
+ dns_name_issubdomain(name, &rpz->nsdname))
+ {
return (DNS_RPZ_TYPE_NSDNAME);
-#endif
+ }
return (DNS_RPZ_TYPE_QNAME);
}
dns_rpz_addr_zbits_t *new_set)
{
dns_rpz_zone_t *rpz;
- char ip_str[DNS_NAME_FORMATSIZE];
+ char ip_str[DNS_NAME_FORMATSIZE], ip2_str[DNS_NAME_FORMATSIZE];
dns_offsets_t ip_name_offsets;
dns_fixedname_t ip_name2f;
dns_name_t ip_name, *ip_name2;
"; invalid leading prefix length", "");
return (ISC_R_FAILURE);
}
- *cp2 = '\0';
if (prefix_num < 1U || prefix_num > 128U) {
badname(log_level, src_name,
"; invalid prefix length of ", prefix_str);
}
/*
- * XXXMUKS: Should the following check be enabled in a
- * production build? It can be expensive for large IP zones
- * from 3rd parties.
- */
-
- /*
- * Convert the address back to a canonical domain name
- * to ensure that the original name is in canonical form.
+ * Complain about bad names but be generous and accept them.
*/
- 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);
+ if (log_level < DNS_RPZ_DEBUG_QUIET &&
+ isc_log_wouldlog(dns_lctx, log_level)) {
+ /*
+ * Convert the address back to a canonical domain name
+ * to ensure that the original name is in canonical form.
+ */
+ 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)) {
+ dns_name_format(ip_name2, ip2_str, sizeof(ip2_str));
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ,
+ DNS_LOGMODULE_RBTDB, log_level,
+ "rpz IP address \"%s\""
+ " is not the canonical \"%s\"",
+ ip_str, ip2_str);
+ }
}
return (ISC_R_SUCCESS);
}
/*
- * Get ready for a new set of policy zones.
+ * Get ready for a new set of policy zones for a view.
*/
isc_result_t
-dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx,
+dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, char *rps_cstr,
+ size_t rps_cstr_size, isc_mem_t *mctx,
isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
{
dns_rpz_zones_t *zones;
if (result != ISC_R_SUCCESS)
goto cleanup_refcount;
- result = dns_rbt_create(mctx, rpz_node_deleter, mctx, &zones->rbt);
+ zones->rps_cstr = rps_cstr;
+ zones->rps_cstr_size = rps_cstr_size;
+#ifdef USE_DNSRPS
+ if (rps_cstr != NULL) {
+ result = dns_dnsrps_view_init(zones, rps_cstr);
+ }
+#else
+ INSIST(!zones->p.dnsrps_enabled);
+#endif
+ if (result == ISC_R_SUCCESS && !zones->p.dnsrps_enabled) {
+ result = dns_rbt_create(mctx, rpz_node_deleter,
+ mctx, &zones->rbt);
+ }
+
if (result != ISC_R_SUCCESS)
goto cleanup_rbt;
*rpzsp = NULL;
isc_refcount_decrement(&rpzs->refs, &refs);
+ if (refs != 0) {
+ return;
+ }
/*
* 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);
+ 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);
}
+ }
+
+ if (rpzs->rps_cstr_size != 0) {
+#ifdef USE_DNSRPS
+ librpz->client_detach(&rpzs->rps_client);
+#endif
+ isc_mem_put(rpzs->mctx, rpzs->rps_cstr,
+ rpzs->rps_cstr_size);
+ }
- cidr_free(rpzs);
+ cidr_free(rpzs);
+ if (rpzs->rbt != NULL) {
dns_rbt_destroy(&rpzs->rbt);
- DESTROYLOCK(&rpzs->maint_lock);
- isc_rwlock_destroy(&rpzs->search_lock);
- isc_refcount_destroy(&rpzs->refs);
- isc_task_destroy(&rpzs->updater);
- isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
}
+ DESTROYLOCK(&rpzs->maint_lock);
+ isc_rwlock_destroy(&rpzs->search_lock);
+ isc_refcount_destroy(&rpzs->refs);
+ isc_task_destroy(&rpzs->updater);
+ isc_mem_putanddetach(&rpzs->mctx, rpzs, sizeof(*rpzs));
}
/*
REQUIRE(rpz != NULL);
RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
- rpz_type = type_from_name(rpz, src_name);
+ rpz_type = type_from_name(rpzs, rpz, src_name);
switch (rpz_type) {
RWLOCK(&rpzs->search_lock, isc_rwlocktype_write);
- rpz_type = type_from_name(rpz, src_name);
+ rpz_type = type_from_name(rpzs, rpz, src_name);
switch (rpz_type) {
case DNS_RPZ_TYPE_QNAME:
* zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
* nxdomain|nodata|cname <domain> ) ]
* [ recursive-only yes|no ] [ log yes|no ]
- * [ max-policy-ttl number ] [ min-update-interval number ] ;
+ * [ max-policy-ttl number ]
+ * [ nsip-enable yes|no ] [ nsdname-enable yes|no ];
* } [ recursive-only yes|no ] [ max-policy-ttl number ]
* [ min-update-interval number ]
* [ break-dnssec yes|no ] [ min-ns-dots number ]
- * [ qname-wait-recurse yes|no ] ;
+ * [ qname-wait-recurse yes|no ]
+ * [ nsip-enable yes|no ] [ nsdname-enable yes|no ]
+ * [ dnsrps-enable yes|no ]
+ * [ dnsrps-options { DNSRPS configuration string } ];
*/
static void
{ "min-update-interval", &cfg_type_uint32, 0 },
{ "policy", &cfg_type_rpz_policy, 0 },
{ "recursive-only", &cfg_type_boolean, 0 },
+ { "nsip-enable", &cfg_type_boolean, 0 },
+ { "nsdname-enable", &cfg_type_boolean, 0 },
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_rpz_tuple = {
{ "nsip-wait-recurse", &cfg_type_boolean, 0 },
{ "qname-wait-recurse", &cfg_type_boolean, 0 },
{ "recursive-only", &cfg_type_boolean, 0 },
+ { "nsip-enable", &cfg_type_boolean, 0 },
+ { "nsdname-enable", &cfg_type_boolean, 0 },
+#ifdef USE_DNSRPS
+ { "dnsrps-enable", &cfg_type_boolean, 0 },
+ { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else
+ { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnsrps-options", &cfg_type_bracketed_text,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
{ NULL, NULL, 0 }
};
static cfg_type_t cfg_type_rpz = {
{ "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
{ "dns64-contact", &cfg_type_astring, 0 },
{ "dns64-server", &cfg_type_astring, 0 },
+#ifdef USE_DNSRPS
+ { "dnsrps-enable", &cfg_type_boolean, 0 },
+ { "dnsrps-options", &cfg_type_bracketed_text, 0 },
+#else
+ { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
+ { "dnsrps-options", &cfg_type_bracketed_text,
+ CFG_CLAUSEFLAG_NOTCONFIGURED },
+#endif
{ "dnssec-accept-expired", &cfg_type_boolean, 0 },
{ "dnssec-enable", &cfg_type_boolean, 0 },
{ "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
#include <dns/db.h>
#include <dns/dlz.h>
#include <dns/dns64.h>
+#include <dns/dnsrps.h>
#include <dns/dnssec.h>
#include <dns/events.h>
#include <dns/message.h>
if (client->query.rpz_st != NULL) {
rpz_st_clear(client);
if (everything) {
+ INSIST(client->query.rpz_st->rpsdb == NULL);
isc_mem_put(client->mctx, client->query.rpz_st,
sizeof(*client->query.rpz_st));
client->query.rpz_st = NULL;
{
char qnamebuf[DNS_NAME_FORMATSIZE];
char p_namebuf[DNS_NAME_FORMATSIZE];
- const char *failed;
- const char *slash;
+ const char *failed, *via, *slash, *str_blank;
const char *rpztypestr1;
const char *rpztypestr2;
rpztypestr2 = "";
}
+ str_blank = (*str != ' ' && *str != '\0') ? " " : "";
+
dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
- dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+
+ if (p_name != NULL) {
+ via = " via ";
+ dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+ } else {
+ via = "";
+ p_namebuf[0] = '\0';
+ }
+
ns_client_log(client, NS_LOGCATEGORY_QUERY_ERRORS,
NS_LOGMODULE_QUERY, level,
- "rpz %s%s%s rewrite %s via %s%s%s%s",
+ "rpz %s%s%s rewrite %s%s%s%s%s%s : %s",
rpztypestr1, slash, rpztypestr2,
- qnamebuf, p_namebuf,
+ qnamebuf, via, p_namebuf, str_blank,
str, failed, isc_result_totext(result));
}
CTRACE(ISC_LOG_DEBUG(3), "rpz_st_clear");
- if (st->m.rdataset != NULL)
+ if (st->m.rdataset != NULL) {
query_putrdataset(client, &st->m.rdataset);
+ }
rpz_match_clear(st);
rpz_clean(NULL, &st->r.db, NULL, NULL);
- if (st->r.ns_rdataset != NULL)
+ if (st->r.ns_rdataset != NULL) {
query_putrdataset(client, &st->r.ns_rdataset);
- if (st->r.r_rdataset != NULL)
+ }
+ if (st->r.r_rdataset != NULL) {
query_putrdataset(client, &st->r.r_rdataset);
+ }
rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL);
- if (st->q.rdataset != NULL)
+ if (st->q.rdataset != NULL) {
query_putrdataset(client, &st->q.rdataset);
- if (st->q.sigrdataset != NULL)
+ }
+ if (st->q.sigrdataset != NULL) {
query_putrdataset(client, &st->q.sigrdataset);
+ }
st->state = 0;
st->m.type = DNS_RPZ_TYPE_BAD;
st->m.policy = DNS_RPZ_POLICY_MISS;
+ if (st->rpsdb != NULL) {
+ dns_db_detach(&st->rpsdb);
+ }
+
}
static dns_rpz_zbits_t
st = client->query.rpz_st;
+#ifdef USE_DNSRPS
+ if (st->popt.dnsrps_enabled) {
+ if (st->rpsdb == NULL ||
+ librpz->have_trig(dns_dnsrps_type2trig(rpz_type),
+ ip_type == dns_rdatatype_aaaa,
+ ((rpsdb_t *)st->rpsdb)->rsp))
+ {
+ return (DNS_RPZ_ALL_ZBITS);
+ }
+ return (0);
+ }
+#endif
+
switch (rpz_type) {
case DNS_RPZ_TYPE_CLIENT_IP:
zbits = st->have.client_ip;
rpz_clean(NULL, dbp, &node, rdatasetp);
version = NULL;
dns_db_attach(client->view->cachedb, dbp);
- result = dns_db_findext(*dbp, name, version, dns_rdatatype_ns,
+ result = dns_db_findext(*dbp, name, version, type,
0, client->now, &node, found,
&cm, &ci, *rdatasetp, NULL);
}
p_name, rpz_type,
" rdatasetiter", result);
CTRACE(ISC_LOG_ERROR,
- "rpz_find_p: rdatasetiter_destroy "
- "failed");
+ "rpz_find_p: rdatasetiter failed");
return (DNS_R_SERVFAIL);
}
/*
SAVE(st->m.version, version);
}
+#ifdef USE_DNSRPS
+/*
+ * Check the results of a RPZ service interface lookup.
+ * Stop after an error (<0) or not a hit on a disabled zone (0).
+ * Continue after a hit on a disabled zone (>0).
+ */
+static int
+dnsrps_ck(librpz_emsg_t *emsg, ns_client_t *client, rpsdb_t *rpsdb,
+ isc_boolean_t recursed)
+{
+ isc_region_t region;
+ librpz_domain_buf_t pname_buf;
+
+ if (!librpz->rsp_result(emsg, &rpsdb->result, recursed, rpsdb->rsp)) {
+ return (-1);
+ }
+
+ /*
+ * Forget the state from before the IP address or domain check
+ * if the lookup hit nothing.
+ */
+ if (rpsdb->result.policy == LIBRPZ_POLICY_UNDEFINED ||
+ rpsdb->result.hit_id != rpsdb->hit_id ||
+ rpsdb->result.policy != LIBRPZ_POLICY_DISABLED)
+ {
+ if (!librpz->rsp_pop_discard(emsg, rpsdb->rsp)) {
+ return (-1);
+ }
+ return (0);
+ }
+
+ /*
+ * Log a hit on a disabled zone.
+ * Forget the zone to not try it again, and restore the pre-hit state.
+ */
+ if (!librpz->rsp_domain(emsg, &pname_buf, rpsdb->rsp)) {
+ return (-1);
+ }
+ region.base = pname_buf.d;
+ region.length = pname_buf.size;
+ dns_name_fromregion(client->query.rpz_st->p_name, ®ion);
+ rpz_log_rewrite(client, ISC_TRUE,
+ dns_dnsrps_2policy(rpsdb->result.zpolicy),
+ dns_dnsrps_trig2type(rpsdb->result.trig), NULL,
+ client->query.rpz_st->p_name, NULL,
+ rpsdb->result.cznum);
+
+ if (!librpz->rsp_forget_zone(emsg, rpsdb->result.cznum, rpsdb->rsp) ||
+ !librpz->rsp_pop(emsg, &rpsdb->result, rpsdb->rsp))
+ {
+ return (-1);
+ }
+ return (1);
+}
+
+/*
+ * Ready the shim database and rdataset for a DNSRPS hit.
+ */
+static isc_boolean_t
+dnsrps_set_p(librpz_emsg_t *emsg, ns_client_t *client, dns_rpz_st_t *st,
+ dns_rdatatype_t qtype, dns_rdataset_t **p_rdatasetp,
+ isc_boolean_t recursed)
+{
+ rpsdb_t *rpsdb;
+ librpz_domain_buf_t pname_buf;
+ isc_region_t region;
+ dns_zone_t *p_zone;
+ dns_db_t *p_db;
+ dns_dbnode_t *p_node;
+ dns_rpz_policy_t policy;
+ dns_fixedname_t foundf;
+ dns_name_t *found;
+ dns_rdatatype_t foundtype, searchtype;
+ isc_result_t result;
+
+ rpsdb = (rpsdb_t *)st->rpsdb;
+
+ if (!librpz->rsp_result(emsg, &rpsdb->result, recursed, rpsdb->rsp)) {
+ return (ISC_FALSE);
+ }
+
+ if (rpsdb->result.policy == LIBRPZ_POLICY_UNDEFINED) {
+ return (ISC_TRUE);
+ }
+
+ /*
+ * Give the fake or shim DNSRPS database its new origin.
+ */
+ if (!librpz->rsp_soa(emsg, NULL, NULL, &rpsdb->origin_buf,
+ &rpsdb->result, rpsdb->rsp))
+ {
+ return (ISC_FALSE);
+ }
+ region.base = rpsdb->origin_buf.d;
+ region.length = rpsdb->origin_buf.size;
+ dns_name_fromregion(&rpsdb->common.origin, ®ion);
+
+ if (!librpz->rsp_domain(emsg, &pname_buf, rpsdb->rsp)) {
+ return (ISC_FALSE);
+ }
+ region.base = pname_buf.d;
+ region.length = pname_buf.size;
+ dns_name_fromregion(st->p_name, ®ion);
+
+ p_zone = NULL;
+ p_db = NULL;
+ p_node = NULL;
+ rpz_ready(client, p_rdatasetp);
+ dns_db_attach(st->rpsdb, &p_db);
+ policy = dns_dnsrps_2policy(rpsdb->result.policy);
+ if (policy != DNS_RPZ_POLICY_RECORD) {
+ result = ISC_R_SUCCESS;
+ } else if (qtype == dns_rdatatype_rrsig) {
+ /*
+ * dns_find_db() refuses to look for and fail to
+ * find dns_rdatatype_rrsig.
+ */
+ result = DNS_R_NXRRSET;
+ policy = DNS_RPZ_POLICY_NODATA;
+ } else {
+ /*
+ * Get the next (and so first) RR from the policy node.
+ * If it is a CNAME, then look for it regardless of the
+ * query type.
+ */
+ if (!librpz->rsp_rr(emsg, &foundtype, NULL, NULL, NULL,
+ &rpsdb->result, rpsdb->qname->ndata,
+ rpsdb->qname->length, rpsdb->rsp))
+ {
+ return (ISC_FALSE);
+ }
+ if (foundtype == dns_rdatatype_cname) {
+ searchtype = dns_rdatatype_cname;
+ } else {
+ searchtype = qtype;
+ }
+ /*
+ * Get the DNSPRS imitation rdataset.
+ */
+ dns_fixedname_init(&foundf);
+ found = dns_fixedname_name(&foundf);
+ result = dns_db_find(p_db, st->p_name, NULL, searchtype,
+ 0, 0, &p_node, found, *p_rdatasetp, NULL);
+
+ if (result == ISC_R_SUCCESS) {
+ if (searchtype == dns_rdatatype_cname &&
+ qtype != dns_rdatatype_cname)
+ {
+ result = DNS_R_CNAME;
+ }
+ } else if (result == DNS_R_NXRRSET) {
+ policy = DNS_RPZ_POLICY_NODATA;
+ } else {
+ snprintf(emsg->c, sizeof(emsg->c), "dns_db_find(): %s",
+ isc_result_totext(result));
+ return (ISC_FALSE);
+ }
+ }
+
+ rpz_save_p(st, client->view->rpzs->zones[rpsdb->result.cznum],
+ dns_dnsrps_trig2type(rpsdb->result.trig), policy,
+ st->p_name, 0, result, &p_zone, &p_db, &p_node,
+ p_rdatasetp, NULL);
+
+ rpz_clean(NULL, NULL, NULL, p_rdatasetp);
+
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+dnsrps_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
+ dns_rpz_type_t rpz_type, dns_rdataset_t **p_rdatasetp)
+{
+ dns_rpz_st_t *st;
+ rpsdb_t *rpsdb;
+ librpz_trig_t trig = LIBRPZ_TRIG_CLIENT_IP;
+ isc_boolean_t recursed = ISC_FALSE;
+ int res;
+ librpz_emsg_t emsg;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ rpsdb = (rpsdb_t *)st->rpsdb;
+
+ result = rpz_ready(client, p_rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (result);
+ }
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ trig = LIBRPZ_TRIG_CLIENT_IP;
+ recursed = ISC_FALSE;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ trig = LIBRPZ_TRIG_IP;
+ recursed = ISC_TRUE;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ trig = LIBRPZ_TRIG_NSIP;
+ recursed = ISC_TRUE;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ do {
+ if (!librpz->rsp_push(&emsg, rpsdb->rsp) ||
+ !librpz->ck_ip(&emsg,
+ netaddr->family == AF_INET
+ ? (const void *)&netaddr->type.in
+ : (const void *)&netaddr->type.in6,
+ netaddr->family, trig, ++rpsdb->hit_id,
+ recursed, rpsdb->rsp) ||
+ (res = dnsrps_ck(&emsg, client, rpsdb, recursed)) < 0)
+ {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+ rpz_type, emsg.c, DNS_R_SERVFAIL);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ }
+ } while (res != 0);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dnsrps_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
+ isc_boolean_t recursed, dns_rpz_type_t rpz_type,
+ dns_rdataset_t **p_rdatasetp)
+{
+ dns_rpz_st_t *st;
+ rpsdb_t *rpsdb;
+ librpz_trig_t trig = LIBRPZ_TRIG_CLIENT_IP;
+ isc_region_t r;
+ int res;
+ librpz_emsg_t emsg;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ rpsdb = (rpsdb_t *)st->rpsdb;
+
+ result = rpz_ready(client, p_rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (result);
+ }
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_QNAME:
+ trig = LIBRPZ_TRIG_QNAME;
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ trig = LIBRPZ_TRIG_NSDNAME;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ dns_name_toregion(trig_name, &r);
+ do {
+ if (!librpz->rsp_push(&emsg, rpsdb->rsp) ||
+ !librpz->ck_domain(&emsg, r.base, r.length,
+ trig, ++rpsdb->hit_id,
+ recursed, rpsdb->rsp) ||
+ (res = dnsrps_ck(&emsg, client, rpsdb, recursed)) < 0)
+ {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+ rpz_type, emsg.c, DNS_R_SERVFAIL);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ }
+ } while (res != 0);
+ return (ISC_R_SUCCESS);
+}
+#endif /* USE_DNSRPS */
+
/*
* Check this address in every eligible policy zone.
*/
CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip");
+ rpzs = client->view->rpzs;
+ st = client->query.rpz_st;
+#ifdef USE_DNSRPS
+ if (st->popt.dnsrps_enabled) {
+ return (dnsrps_rewrite_ip(client, netaddr, rpz_type,
+ p_rdatasetp));
+ }
+#endif
+
dns_fixedname_init(&ip_namef);
ip_name = dns_fixedname_name(&ip_namef);
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);
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_zbits_t allowed_zbits, isc_boolean_t recursed,
+ dns_rdataset_t **rdatasetp)
{
dns_rpz_zones_t *rpzs;
dns_rpz_zone_t *rpz;
dns_rpz_policy_t policy;
isc_result_t result;
+#ifndef USE_DNSRPS
+ UNUSED(recursed);
+#endif
+
CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_name");
+ rpzs = client->view->rpzs;
+ st = client->query.rpz_st;
+
+#ifdef USE_DNSRPS
+ if (st->popt.dnsrps_enabled) {
+ return (dnsrps_rewrite_name(client, trig_name, recursed,
+ rpz_type, rdatasetp));
+ }
+#endif
+
zbits = rpz_get_zbits(client, qtype, rpz_type);
zbits &= allowed_zbits;
if (zbits == 0)
return (ISC_R_SUCCESS);
- rpzs = client->view->rpzs;
-
/*
* Use the summary database to find the bit mask of policy zones
* with policies for this trigger name. We do this even if there
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.
st->r.label--;
}
+/*
+ * RPZ query result types
+ */
+typedef enum {
+ qresult_type_done = 0,
+ qresult_type_restart = 1,
+ qresult_type_recurse = 2
+} qresult_type_t;
+
/*
* Look for response policy zone QNAME, NSIP, and NSDNAME rewriting.
*/
dns_rdataset_t *rdataset;
dns_fixedname_t nsnamef;
dns_name_t *nsname;
- int qresult_type;
+ qresult_type_t qresult_type;
dns_rpz_zbits_t zbits;
isc_result_t result = ISC_R_SUCCESS;
dns_rpz_have_t have;
dns_rpz_popt_t popt;
int rpz_ver;
+#ifdef USE_DNSRPS
+ librpz_emsg_t emsg;
+#endif
CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite");
return (DNS_R_DISALLOWED);
RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
- if (rpzs->p.num_zones == 0 ||
+ if ((rpzs->p.num_zones == 0 && !rpzs->p.dnsrps_enabled) ||
(!RECURSIONOK(client) && rpzs->p.no_rd_ok == 0) ||
!rpz_ck_dnssec(client, qresult, ordataset, osigset))
{
if (st == NULL)
return (ISC_R_NOMEMORY);
st->state = 0;
+ st->rpsdb = NULL;
}
if (st->state == 0) {
st->state |= DNS_RPZ_ACTIVE;
st->popt = popt;
st->rpz_ver = rpz_ver;
client->query.rpz_st = st;
+ if (popt.dnsrps_enabled) {
+#ifdef USE_DNSRPS
+ if (st->rpsdb != NULL) {
+ dns_db_detach(&st->rpsdb);
+ }
+ result = dns_dnsrps_rewrite_init(&emsg, st, rpzs,
+ client->query.qname,
+ client->mctx,
+ RECURSIONOK(client));
+ if (result != ISC_R_SUCCESS) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
+ NULL, DNS_RPZ_TYPE_QNAME,
+ emsg.c, result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (ISC_R_SUCCESS);
+ }
+#else
+ INSIST(0);
+#endif
+ }
}
/*
case ISC_R_SUCCESS:
case DNS_R_GLUE:
case DNS_R_ZONECUT:
- qresult_type = 0;
+ qresult_type = qresult_type_done;
break;
case DNS_R_EMPTYNAME:
case DNS_R_NXRRSET:
case DNS_R_NCACHENXRRSET:
case DNS_R_CNAME:
case DNS_R_DNAME:
- qresult_type = 1;
+ qresult_type = qresult_type_restart;
break;
case DNS_R_DELEGATION:
case ISC_R_NOTFOUND:
* can rewrite.
*/
if (RECURSIONOK(client))
- qresult_type = 2;
+ qresult_type = qresult_type_recurse;
else
- qresult_type = 1;
+ qresult_type = qresult_type_restart;
break;
case ISC_R_FAILURE:
case ISC_R_TIMEDOUT:
case DNS_R_BROKENCHAIN:
- rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, NULL,
DNS_RPZ_TYPE_QNAME,
" stop on qresult in rpz_rewrite()", qresult);
return (ISC_R_SUCCESS);
default:
- rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, NULL,
DNS_RPZ_TYPE_QNAME,
" stop on unrecognized qresult in rpz_rewrite()",
qresult);
rdataset = NULL;
if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) !=
- (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) {
+ (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME))
+ {
isc_netaddr_t netaddr;
dns_rpz_zbits_t allowed;
- if (qresult_type == 2) {
+ if (!st->popt.dnsrps_enabled &&
+ qresult_type == qresult_type_recurse)
+ {
/*
* This request needs recursion that has not been done.
* Get bits for the policy zones that do not need
* to wait for the results of recursion.
*/
allowed = st->have.qname_skip_recurse;
- if (allowed == 0)
+ if (allowed == 0) {
return (ISC_R_SUCCESS);
+ }
} else {
allowed = DNS_RPZ_ALL_ZBITS;
}
* There is a first time for each name in a CNAME chain
*/
if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+ isc_boolean_t norec =
+ ISC_TF(qresult_type != qresult_type_recurse);
result = rpz_rewrite_name(client, client->query.qname,
qtype, DNS_RPZ_TYPE_QNAME,
- allowed, &rdataset);
+ allowed, norec,
+ &rdataset);
if (result != ISC_R_SUCCESS)
goto cleanup;
* Do not bother saving the work from this attempt,
* because recusion is so slow.
*/
- if (qresult_type == 2)
+ if (qresult_type == qresult_type_recurse)
goto cleanup;
/*
}
/*
- * Check known IP addresses for the query name if the database
- * lookup resulted in some addresses (qresult_type == 0)
+ * Check known IP addresses for the query name if the database lookup
+ * resulted in some addresses (qresult_type == qresult_type_done)
* 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 && qresult_type == 0 &&
+ if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 &&
+ qresult_type == qresult_type_done &&
rpz_get_zbits(client, qtype, DNS_RPZ_TYPE_IP) != 0) {
result = rpz_rewrite_ip_rrsets(client,
client->query.qname, qtype,
case ISC_R_FAILURE:
rpz_rewrite_ns_skip(client, nsname, result,
DNS_RPZ_DEBUG_LEVEL3,
- " NS rpz_rrset_find() ");
+ " NS rpz_rrset_find()");
continue;
default:
rpz_rewrite_ns_skip(client, nsname, result,
DNS_RPZ_INFO_LEVEL,
" unrecognized NS"
- " rpz_rrset_find() ");
+ " rpz_rrset_find()");
continue;
}
}
qtype,
DNS_RPZ_TYPE_NSDNAME,
DNS_RPZ_ALL_ZBITS,
+ ISC_TRUE,
&rdataset);
if (result != ISC_R_SUCCESS) {
dns_rdata_freestruct(&ns);
result = ISC_R_SUCCESS;
cleanup:
+#ifdef USE_DNSRPS
+ if (st->popt.dnsrps_enabled &&
+ st->m.policy != DNS_RPZ_POLICY_ERROR &&
+ !dnsrps_set_p(&emsg, client, st, qtype, &rdataset,
+ ISC_TF(qresult_type != qresult_type_recurse)))
+ {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, NULL,
+ DNS_RPZ_TYPE_BAD, emsg.c, DNS_R_SERVFAIL);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ }
+#endif
if (st->m.policy != DNS_RPZ_POLICY_MISS &&
st->m.policy != DNS_RPZ_POLICY_ERROR &&
st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN)
if (qctx->rpz_st->rpz_ver != qctx->client->view->rpzs->rpz_ver)
{
ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT,
- NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ NS_LOGMODULE_QUERY,
+ DNS_RPZ_INFO_LEVEL,
"query_resume: RPZ settings "
"out of date "
"(rpz_ver %d, expected %d)",
RESTORE(qctx->version, qctx->rpz_st->m.version);
RESTORE(qctx->zone, qctx->rpz_st->m.zone);
+ /*
+ * Add SOA record to additional section
+ */
+ rresult = query_addsoa(qctx,
+ dns_rdataset_isassociated(qctx->rdataset),
+ DNS_SECTION_ADDITIONAL);
+ if (rresult != ISC_R_SUCCESS) {
+ QUERY_ERROR(qctx, result);
+ return (ISC_R_COMPLETE);
+ }
+
switch (qctx->rpz_st->m.policy) {
case DNS_RPZ_POLICY_TCP_ONLY:
qctx->client->message->flags |= DNS_MESSAGEFLAG_TC;
*/
isc_result_t
query_sign_nodata(query_ctx_t *qctx) {
- dns_section_t section;
isc_result_t result;
/*
* Look for a NSEC3 record if we don't have a NSEC record.
}
/*
- * Add SOA to the additional section if generated by a RPZ
- * rewrite.
+ * The RPZ SOA has already been added to the additional section
+ * if this was an RPZ rewrite, but if it wasn't, add it now.
*/
- section = qctx->nxrewrite ? DNS_SECTION_ADDITIONAL
- : DNS_SECTION_AUTHORITY;
-
- result = query_addsoa(qctx, ISC_UINT32_MAX, section);
- if (result != ISC_R_SUCCESS) {
- QUERY_ERROR(qctx, result);
- return (query_done(qctx));
+ if (!qctx->nxrewrite) {
+ result = query_addsoa(qctx, ISC_UINT32_MAX,
+ DNS_SECTION_AUTHORITY);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(qctx, result);
+ return (query_done(qctx));
+ }
}
/*