From: Evan Hunt Date: Mon, 11 Sep 2017 18:53:42 +0000 (-0700) Subject: [master] DNS Response Policy Service API X-Git-Tag: v9.12.0a1~21 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=3363f3147a2774675800db182af2ea5d3c456e85;p=thirdparty%2Fbind9.git [master] DNS Response Policy Service API 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] --- diff --git a/CHANGES b/CHANGES index 92cb845c5f0..79cc3fa4184 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +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] diff --git a/bin/named/server.c b/bin/named/server.c index bbd9ee726bd..323e7f526cf 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -1862,6 +1863,230 @@ cleanup: 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) @@ -1960,13 +2185,19 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element, "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)) @@ -2030,12 +2261,17 @@ configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element, } 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; @@ -2051,13 +2287,80 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj, 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)) @@ -2141,15 +2444,33 @@ configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_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); } @@ -3429,7 +3750,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, 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; @@ -3460,6 +3781,32 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, 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 @@ -5567,10 +5914,14 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, } /* - * 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; } @@ -8315,6 +8666,22 @@ load_configuration(const char *filename, named_server_t *server, (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: @@ -8757,6 +9124,12 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { 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); @@ -8913,6 +9286,10 @@ named_server_destroy(named_server_t **serverp) { 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); diff --git a/bin/tests/system/Makefile.in b/bin/tests/system/Makefile.in index 6370c67ba3b..e7fedd3a53d 100644 --- a/bin/tests/system/Makefile.in +++ b/bin/tests/system/Makefile.in @@ -12,7 +12,7 @@ VERSION=@BIND9_VERSION@ @BIND9_MAKE_INCLUDES@ -SUBDIRS = dlzexternal dyndb pipelined rndc rsabigexponent tkey +SUBDIRS = dlzexternal dyndb pipelined rndc rpz rsabigexponent tkey CINCLUDES = ${ISC_INCLUDES} ${DNS_INCLUDES} diff --git a/bin/tests/system/feature-test.c b/bin/tests/system/feature-test.c index bf1928a0710..2b831972b04 100644 --- a/bin/tests/system/feature-test.c +++ b/bin/tests/system/feature-test.c @@ -35,6 +35,7 @@ usage() { fprintf(stderr, "usage: feature-test \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"); @@ -54,6 +55,14 @@ main(int argc, char **argv) { 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); diff --git a/bin/tests/system/rpz/.gitignore b/bin/tests/system/rpz/.gitignore new file mode 100644 index 00000000000..04570880160 --- /dev/null +++ b/bin/tests/system/rpz/.gitignore @@ -0,0 +1,2 @@ +alt-dnsrpzd-license.conf +dnsrps diff --git a/bin/tests/system/rpz/Makefile.in b/bin/tests/system/rpz/Makefile.in new file mode 100644 index 00000000000..abee0320467 --- /dev/null +++ b/bin/tests/system/rpz/Makefile.in @@ -0,0 +1,45 @@ +# 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} + diff --git a/bin/tests/system/rpz/ckdnsrps.sh b/bin/tests/system/rpz/ckdnsrps.sh new file mode 100644 index 00000000000..fa239ee47b3 --- /dev/null +++ b/bin/tests/system/rpz/ckdnsrps.sh @@ -0,0 +1,161 @@ +#! /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 <>$MCONF +$MASTER }; +EOF + +# write dnsrps setttings for resolvers that should not start dnsrpzd +cat <>$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' diff --git a/bin/tests/system/rpz/clean.sh b/bin/tests/system/rpz/clean.sh index ec469037bb0..ffe1c4fd900 100644 --- a/bin/tests/system/rpz/clean.sh +++ b/bin/tests/system/rpz/clean.sh @@ -10,7 +10,9 @@ rm -f proto.* dsset-* trusted.conf dig.out* nsupdate.tmp ns*/*tmp 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 diff --git a/bin/tests/system/rpz/dnsrps.c b/bin/tests/system/rpz/dnsrps.c new file mode 100644 index 00000000000..1abd9d848ed --- /dev/null +++ b/bin/tests/system/rpz/dnsrps.c @@ -0,0 +1,164 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USE_DNSRPS +#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN +#include + +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 +} diff --git a/bin/tests/system/rpz/dnsrpzd-license.conf b/bin/tests/system/rpz/dnsrpzd-license.conf new file mode 100644 index 00000000000..745c7c9757f --- /dev/null +++ b/bin/tests/system/rpz/dnsrpzd-license.conf @@ -0,0 +1,10 @@ +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"; +}; diff --git a/bin/tests/system/rpz/dnsrpzd.conf b/bin/tests/system/rpz/dnsrpzd.conf new file mode 100644 index 00000000000..de73945ed10 --- /dev/null +++ b/bin/tests/system/rpz/dnsrpzd.conf @@ -0,0 +1,49 @@ +# 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; }; }; diff --git a/bin/tests/system/rpz/fastrpz.license b/bin/tests/system/rpz/fastrpz.license new file mode 100644 index 00000000000..634c55359b0 --- /dev/null +++ b/bin/tests/system/rpz/fastrpz.license @@ -0,0 +1,3 @@ +NAME='isc.license.fastrpz.com' +KEY='hmac-sha256:farsight_fastrpz_license:f405d02b4c8af54855fcebc1' +LSERVER=license1.fastrpz.com diff --git a/bin/tests/system/rpz/ns2/named.conf b/bin/tests/system/rpz/ns2/named.conf index 1cb99b315c5..d30551df4af 100644 --- a/bin/tests/system/rpz/ns2/named.conf +++ b/bin/tests/system/rpz/ns2/named.conf @@ -43,4 +43,5 @@ zone "subsub.sub3.tld2." {type master; file "tld2.db";}; 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;}; diff --git a/bin/tests/system/rpz/ns3/base.db b/bin/tests/system/rpz/ns3/base.db index 30425bb3830..3adb543fb28 100644 --- a/bin/tests/system/rpz/ns3/base.db +++ b/bin/tests/system/rpz/ns3/base.db @@ -15,3 +15,5 @@ $TTL 300 ; regression testing for some old crashes example.com NS example.org. + +domain.com cname foobar.com diff --git a/bin/tests/system/rpz/ns3/named.conf b/bin/tests/system/rpz/ns3/named.conf index 5bb696b7c17..866d5feb662 100644 --- a/bin/tests/system/rpz/ns3/named.conf +++ b/bin/tests/system/rpz/ns3/named.conf @@ -21,7 +21,7 @@ options { session-keyfile "session.key"; listen-on { 10.53.0.3; }; listen-on-v6 { none; }; - notify no; + notify yes; minimal-responses no; response-policy { @@ -43,9 +43,17 @@ options { 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; @@ -87,5 +95,5 @@ zone "bl-tcp-only." {type master; file "bl-tcp-only.db"; 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;}; diff --git a/bin/tests/system/rpz/ns5/named.conf b/bin/tests/system/rpz/ns5/named.conf index dd8cfa8ae13..22e56cb0eda 100644 --- a/bin/tests/system/rpz/ns5/named.conf +++ b/bin/tests/system/rpz/ns5/named.conf @@ -22,12 +22,14 @@ options { 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 { diff --git a/bin/tests/system/rpz/ns5/tld5.db b/bin/tests/system/rpz/ns5/tld5.db index 00a5e9b00a8..432b3e6de0b 100644 --- a/bin/tests/system/rpz/ns5/tld5.db +++ b/bin/tests/system/rpz/ns5/tld5.db @@ -25,3 +25,5 @@ example.tld5. NS ns 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" diff --git a/bin/tests/system/rpz/ns6/named.conf b/bin/tests/system/rpz/ns6/named.conf index e7015e7f1fa..064b3cdbaeb 100644 --- a/bin/tests/system/rpz/ns6/named.conf +++ b/bin/tests/system/rpz/ns6/named.conf @@ -20,9 +20,17 @@ options { 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; @@ -38,5 +46,7 @@ zone "policy1" { type slave; masters { 10.53.0.5; }; file "empty.db"; + also-notify { 10.53.0.3 port 5301; }; + notify-delay 0; allow-transfer { any; }; }; diff --git a/bin/tests/system/rpz/ns7/named.conf b/bin/tests/system/rpz/ns7/named.conf index 54627736b8e..379eff1dec5 100644 --- a/bin/tests/system/rpz/ns7/named.conf +++ b/bin/tests/system/rpz/ns7/named.conf @@ -18,11 +18,18 @@ options { 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; @@ -38,6 +45,8 @@ zone "policy2" { 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 }; diff --git a/bin/tests/system/rpz/setup.sh b/bin/tests/system/rpz/setup.sh index 5a87d15a420..2cadd168a14 100644 --- a/bin/tests/system/rpz/setup.sh +++ b/bin/tests/system/rpz/setup.sh @@ -6,6 +6,8 @@ # 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=.. @@ -13,7 +15,28 @@ 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 @@ -48,9 +71,11 @@ response-policy { 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 @@ -110,3 +135,11 @@ $PERL -e 'for ($cnt = $val = 1; $cnt <= 3000; ++$cnt) { 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 diff --git a/bin/tests/system/rpz/test3 b/bin/tests/system/rpz/test3 index 6d87d7a1732..0cdc7524b95 100644 --- a/bin/tests/system/rpz/test3 +++ b/bin/tests/system/rpz/test3 @@ -36,4 +36,7 @@ update add a4-1.tld2.bl. 300 A 12.12.12.12 ; prefer policy for largest NS name update add ns.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.1 update add ns.subsub.sub3.tld2.rpz-nsdname.bl. 300 A 127.0.0.2 + +; ip-as-qname rewrites all of tld5 +update add ns.tld5.bl. 300 A 12.12.12.12 send diff --git a/bin/tests/system/rpz/test4 b/bin/tests/system/rpz/test4 index 4129be626ca..beca0d3bf0e 100644 --- a/bin/tests/system/rpz/test4 +++ b/bin/tests/system/rpz/test4 @@ -25,4 +25,7 @@ update add 24.0.0.168.192.rpz-ip.bl 300 CNAME 24.0.0.168.192. ; ; prefer NSIP policy to NSDNAME policy update add ns.tld2.rpz-nsdname.bl. 300 CNAME 10.0.0.1 + +; ip-as-ns rewrites all of tld5 +update add 32.5.0.53.10.rpz-ip.bl. 300 A 12.12.12.12 send diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh index 9112b3230a6..38e5a6a55b7 100644 --- a/bin/tests/system/rpz/tests.sh +++ b/bin/tests/system/rpz/tests.sh @@ -1,14 +1,16 @@ +#!/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 @@ -22,13 +24,20 @@ ns6=$ns.6 # a forwarding server 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 @@ -48,8 +57,51 @@ comment () { 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 @@ -57,9 +109,9 @@ digcmd () { # 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 @@ -69,6 +121,7 @@ digcmd () { 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 @@ -83,6 +136,72 @@ setret () { 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 @@ -118,6 +237,7 @@ restart () { fi $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns$1 load_db + dnsrps_loaded } # $1=server and irrelevant args $2=error message @@ -179,6 +299,7 @@ start_group () { else GROUP_NM= fi + dnsrps_loaded TEST_NUM=0 } @@ -189,6 +310,7 @@ end_group () { TEST_FILE= fi ckalive $ns3 "I:failed; ns3 server crashed and restarted" + dnsrps_loaded GROUP_NM= } @@ -257,7 +379,7 @@ addr () { 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 @@ -306,9 +428,6 @@ drop () { 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 @@ -353,29 +472,28 @@ ckstats $ns5 test1 ns5 1 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 @@ -399,25 +517,13 @@ nxdomain c2.crash2.tld3 # 16 assert in rbtdb.c 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 @@ -438,47 +544,53 @@ nochange a5-1-2.tld2 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 @@ -530,7 +642,6 @@ end_group ckstats $ns3 bugs ns3 8 - # superficial test for major performance bugs QPERF=`sh qperf.sh` if test -n "$QPERF"; then @@ -538,10 +649,10 @@ 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 \ @@ -558,7 +669,6 @@ if test -n "$QPERF"; then # 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` @@ -583,6 +693,27 @@ else 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 @@ -604,7 +735,9 @@ $DIG +noall +answer -p 5300 @$ns3 any a3-2.tld2 > dig.out.any 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() { @@ -617,26 +750,40 @@ send 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 diff --git a/bin/tests/system/rpzrecurse/clean.sh b/bin/tests/system/rpzrecurse/clean.sh index 7800df5ef18..3c23011d5fe 100644 --- a/bin/tests/system/rpzrecurse/clean.sh +++ b/bin/tests/system/rpzrecurse/clean.sh @@ -9,9 +9,11 @@ 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* diff --git a/bin/tests/system/rpzrecurse/ns1/named.conf b/bin/tests/system/rpzrecurse/ns1/named.conf index 2b44084a641..012e6cb2d78 100644 --- a/bin/tests/system/rpzrecurse/ns1/named.conf +++ b/bin/tests/system/rpzrecurse/ns1/named.conf @@ -11,6 +11,7 @@ options { 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; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.clientip.conf b/bin/tests/system/rpzrecurse/ns2/named.clientip.conf index 6c5376b3d38..3245032ad5f 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.clientip.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.clientip.conf @@ -19,7 +19,9 @@ view "recursive" { 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.clientip2.conf b/bin/tests/system/rpzrecurse/ns2/named.clientip2.conf index 2a429816881..a987854c984 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.clientip2.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.clientip2.conf @@ -20,7 +20,9 @@ view "recursive" { # 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.conf.header b/bin/tests/system/rpzrecurse/ns2/named.conf.header index 364d45c93bf..09608bf68f6 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.conf.header +++ b/bin/tests/system/rpzrecurse/ns2/named.conf.header @@ -13,11 +13,15 @@ options { 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 { diff --git a/bin/tests/system/rpzrecurse/ns2/named.log.conf b/bin/tests/system/rpzrecurse/ns2/named.log.conf index 62a82d82dd3..52b4b4fa3af 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.log.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.log.conf @@ -20,7 +20,9 @@ view "recursive" { 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf b/bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf index 0174745c190..cc73a5fae1d 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.wildcard1.conf @@ -18,7 +18,9 @@ view "recursive" { # 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf b/bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf index 0547fc251ee..f20a77e606c 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.wildcard2.conf @@ -19,7 +19,9 @@ view "recursive" { 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf b/bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf index 27a0e991688..801d7479a3a 100644 --- a/bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf +++ b/bin/tests/system/rpzrecurse/ns2/named.wildcard3.conf @@ -18,7 +18,9 @@ view "recursive" { # 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns3/named1.conf b/bin/tests/system/rpzrecurse/ns3/named1.conf index ba241971e6e..fada50b98c0 100644 --- a/bin/tests/system/rpzrecurse/ns3/named1.conf +++ b/bin/tests/system/rpzrecurse/ns3/named1.conf @@ -17,11 +17,17 @@ options { 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns3/named2.conf b/bin/tests/system/rpzrecurse/ns3/named2.conf index 63f64ea7400..7d3292b8fe8 100644 --- a/bin/tests/system/rpzrecurse/ns3/named2.conf +++ b/bin/tests/system/rpzrecurse/ns3/named2.conf @@ -21,7 +21,12 @@ options { 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"; }; diff --git a/bin/tests/system/rpzrecurse/ns4/named.conf b/bin/tests/system/rpzrecurse/ns4/named.conf index 33cb012b6df..d679aae283e 100644 --- a/bin/tests/system/rpzrecurse/ns4/named.conf +++ b/bin/tests/system/rpzrecurse/ns4/named.conf @@ -13,6 +13,7 @@ options { 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; }; diff --git a/bin/tests/system/rpzrecurse/prereq.sh b/bin/tests/system/rpzrecurse/prereq.sh index 50903bc54f5..84b419bf60f 100644 --- a/bin/tests/system/rpzrecurse/prereq.sh +++ b/bin/tests/system/rpzrecurse/prereq.sh @@ -9,13 +9,4 @@ 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 diff --git a/bin/tests/system/rpzrecurse/setup.sh b/bin/tests/system/rpzrecurse/setup.sh index e3972b3f70e..fc94134f7b1 100644 --- a/bin/tests/system/rpzrecurse/setup.sh +++ b/bin/tests/system/rpzrecurse/setup.sh @@ -6,9 +6,52 @@ # 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 <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 diff --git a/bin/tests/system/rpzrecurse/testgen.pl b/bin/tests/system/rpzrecurse/testgen.pl index 987501aa7f3..14491febc7a 100755 --- a/bin/tests/system/rpzrecurse/testgen.pl +++ b/bin/tests/system/rpzrecurse/testgen.pl @@ -1,10 +1,18 @@ #!/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; @@ -24,13 +32,13 @@ view "recursive" { 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 diff --git a/bin/tests/system/rpzrecurse/tests.sh b/bin/tests/system/rpzrecurse/tests.sh index 9772699cab9..6566e13f9c6 100644 --- a/bin/tests/system/rpzrecurse/tests.sh +++ b/bin/tests/system/rpzrecurse/tests.sh @@ -6,12 +6,75 @@ # 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 @@ -47,8 +110,8 @@ expect_norecurse() { 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 } } @@ -62,11 +125,14 @@ expect_recurse() { 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} @@ -177,6 +243,7 @@ echo "I:adding an NSDNAME policy" 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})" @@ -185,6 +252,7 @@ sleep 1 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 @@ -198,8 +266,8 @@ for n in 1 2 3 4 5 6 7 8 9; do 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 @@ -222,6 +290,7 @@ kill -TSTP $PID 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})" @@ -230,6 +299,7 @@ sleep 1 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 @@ -243,8 +313,8 @@ for n in 1 2 3 4 5 6 7 8 9; do 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 @@ -381,5 +451,10 @@ if test $p1 -le $p2; then ret=1; fi 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 diff --git a/config.h.in b/config.h.in index 89fdb143fa5..9cb97efadf2 100644 --- a/config.h.in +++ b/config.h.in @@ -155,16 +155,22 @@ int sigwait(const unsigned int *set, int *sig); /* 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. */ @@ -530,6 +536,9 @@ int sigwait(const unsigned int *set, int *sig); /* 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 @@ -578,6 +587,9 @@ int sigwait(const unsigned int *set, int *sig); /* 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 diff --git a/configure b/configure index 6c83d21593c..545c541d5cc 100755 --- a/configure +++ b/configure @@ -935,6 +935,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -1022,6 +1023,10 @@ enable_atomic 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 @@ -1092,6 +1097,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1344,6 +1350,15 @@ do | -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=* \ @@ -1481,7 +1496,7 @@ fi 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. @@ -1634,6 +1649,7 @@ Fine tuning of the installation directories: --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] @@ -1702,8 +1718,10 @@ Optional Features: --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] @@ -1743,6 +1761,8 @@ Optional Packages: --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 @@ -20613,6 +20633,232 @@ $as_echo "#define ENABLE_RPZ_NSDNAME 1" >>confdefs.h ;; 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? # @@ -23306,7 +23552,7 @@ ac_config_commands="$ac_config_commands chmod" # 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" # @@ -24376,6 +24622,7 @@ do "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" ;; @@ -25805,6 +26052,8 @@ report() { 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" && \ @@ -25851,6 +26100,9 @@ report() { 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)" diff --git a/configure.in b/configure.in index 24f83fe3b8a..4c27d498c91 100644 --- a/configure.in +++ b/configure.in @@ -4235,13 +4235,13 @@ esac # 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) ;; @@ -4253,13 +4253,13 @@ esac # 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) ;; @@ -4267,6 +4267,66 @@ case "$enable_nsdname" in ;; 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? # @@ -5165,6 +5225,7 @@ AC_CONFIG_FILES([ 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 @@ -5321,6 +5382,8 @@ report() { 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" && \ @@ -5367,6 +5430,9 @@ report() { 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)" diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 71eef5405c0..7a18da97b16 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4521,7 +4521,9 @@ badresp:1,adberr:0,findfail:0,valfail:0] [ recursive-only yes_or_no ] [ log yes_or_no ] [ max-policy-ttl number ] - [ min-update-interval number ] ; + [ min-update-interval number ] + [ nsip-enable yes_or_no ] + [ nsdname-enable yes_or_no ] ; ... } [ recursive-only yes_or_no ] [ max-policy-ttl number ] @@ -4529,7 +4531,11 @@ badresp:1,adberr:0,findfail:0,valfail:0] [ break-dnssec yes_or_no ] [ min-ns-dots number ] [ nsip-wait-recurse yes_or_no ] - [ qname-wait-recurse yes_or_no ] ; ] + [ qname-wait-recurse yes_or_no ] + [ nsip-enable yes_or_no ] + [ nsdname-enable yes_or_no ] + [ dnsrps-enable yes_or_no ] + [ dnsrps-options { parameters } ] ; ] [ catalog-zones { zone quoted_string [ [ port ip_port ] [ dscp ip_dscp ] { @@ -10212,6 +10218,9 @@ deny-answer-aliases { "example.net"; }; NSIP triggers match IP addresses in A and AAAA RRsets for domains that can be checked against NSDNAME policy records. + The nsdname-enable phrase turns NSDNAME + triggers off or on for a single policy zone or all + zones. @@ -10227,6 +10236,9 @@ deny-answer-aliases { "example.net"; }; least min-ns-dots dots. The default value of min-ns-dots is 1, to exclude top level domains. + The nsip-enable phrase turns NSIP + triggers off or on for a single policy zone or all + zones. If a name server's IP address is not yet known, @@ -10475,11 +10487,11 @@ deny-answer-aliases { "example.net"; }; 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 qname-wait-recurse no option - overrides that default behavior when recursion cannot - change a non-error response. + The qname-wait-recurse yes 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 @@ -10493,6 +10505,42 @@ deny-answer-aliases { "example.net"; }; discover problems at the authoritative server. + + The dnsrps-enable yes option turns on + the DNS Rsponse Policy Service (DNSRPS) interface, if it has been + compiled in to named using + configure --enable-dnsrps. + + + + The dnsrps-options block provides additional + RPZ configuration settings, which are passed through to the + DNSRPS provider library. + Multiple DNSRPS settings in an dnsrps-options + string should be separated with semi-colons. + The DNSRPS provider, librpz, is passed a configuration string + consisting of the dnsrps-options text, + concatenated with settings derived from the + response-policy statement. + + + + Note: The dnsrps-options 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 + dnsrpzd-conf, + dnsrpzd-sock, and + dnzrpzd-args (for details of these options, + see the librpz documentation). + Other RPZ configuration settings could be included in + dnsrps-options + as well, but if named were switched + back to traditional RPZ by setting + dnsrps-enable to "no", those options would + be ignored. + + 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 diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 308e5a174c2..f5244f99a14 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -181,6 +181,29 @@
New Features + + + Added support for the DNS Response Policy Service (DNSRPS) API, + a mechanism to allow named 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 named, particularly when using + large and frequently-updated policy zones. It also enables + named to share response policy providers + with other DNS implementations such as Unbound. + + + This feature is avaiable if BIND is built with + configure --enable-dnsrps + and if dnsrps-enable is set to "yes" in + named.conf. + + + Thanks to Vernon Schryver and Farsight Security for the + contribution. [RT #43376] + + Code implementing name server query processing has been moved diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index 213e82322df..479396bd146 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -60,8 +60,9 @@ DNSTAPOBJS = dnstap.@O@ dnstap.pb-c.@O@ 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@ \ @@ -103,7 +104,7 @@ DNSTAPSRCS = dnstap.c dnstap.pb-c.c 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 \ diff --git a/lib/dns/dnsrps.c b/lib/dns/dnsrps.c new file mode 100644 index 00000000000..4c38732a506 --- /dev/null +++ b/lib/dns/dnsrps.c @@ -0,0 +1,995 @@ +/* + * 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 + +#ifdef USE_DNSRPS + +#include +#include +#include + +#include +#define LIBRPZ_LIB_OPEN DNSRPS_LIB_OPEN +#include +#include +#include +#include +#include + +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 */ diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index 0f13808f91c..dec7a7b8612 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -14,12 +14,12 @@ HEADERS = acl.h adb.h badcache.h bit.h byaddr.h \ 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 \ diff --git a/lib/dns/include/dns/dnsrps.h b/lib/dns/include/dns/dnsrps.h new file mode 100644 index 00000000000..f0acc4f5c48 --- /dev/null +++ b/lib/dns/include/dns/dnsrps.h @@ -0,0 +1,95 @@ +#ifndef DNS_DNSRPS_H +#define DNS_DNSRPS_H + +#include +#include + +#include + +#ifdef USE_DNSRPS + +#include +#include + +/* + * 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 */ diff --git a/lib/dns/include/dns/librpz.h b/lib/dns/include/dns/librpz.h new file mode 100644 index 00000000000..75778a30067 --- /dev/null +++ b/lib/dns/include/dns/librpz.h @@ -0,0 +1,963 @@ +/* + * 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 +#include +#include +#include +#include +#include + + +/* + * 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 + +/** + * 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 */ diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h index 9c0d22c8ad1..2fd604128a4 100644 --- a/lib/dns/include/dns/rpz.h +++ b/lib/dns/include/dns/rpz.h @@ -6,8 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* $Id$ */ - #ifndef DNS_RPZ_H #define DNS_RPZ_H 1 @@ -187,6 +185,9 @@ typedef struct dns_rpz_popt dns_rpz_popt_t; 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; @@ -203,8 +204,9 @@ struct dns_rpz_zones { 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; @@ -250,6 +252,13 @@ struct dns_rpz_zones { 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; }; @@ -319,6 +328,11 @@ typedef struct { 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 @@ -360,7 +374,8 @@ dns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset, 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 diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c index 0c8e4535782..06d97532c2b 100644 --- a/lib/dns/rpz.c +++ b/lib/dns/rpz.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -397,11 +398,10 @@ fix_qname_skip_recurse(dns_rpz_zones_t *rpzs) { * 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 @@ -681,6 +681,12 @@ badname(int level, const dns_name_t *name, const char *str1, const char *str2) { * 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, @@ -693,7 +699,7 @@ 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)) { @@ -703,43 +709,53 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix, (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); } } @@ -750,26 +766,31 @@ ip2name(const dns_rpz_cidr_key_t *tgt_ip, dns_rpz_prefix_t tgt_prefix, } /* - * 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); } @@ -787,7 +808,7 @@ name2ipkey(int log_level, 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; @@ -830,7 +851,6 @@ name2ipkey(int log_level, "; 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); @@ -926,21 +946,27 @@ name2ipkey(int log_level, } /* - * 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); @@ -1396,10 +1422,11 @@ rpz_node_deleter(void *nm_data, void *mctx) { } /* - * 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; @@ -1424,7 +1451,20 @@ dns_rpz_new_zones(dns_rpz_zones_t **rpzsp, isc_mem_t *mctx, 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; @@ -2074,26 +2114,38 @@ dns_rpz_detach_rpzs(dns_rpz_zones_t **rpzsp) { *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)); } /* @@ -2140,7 +2192,7 @@ dns_rpz_add(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, 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) { @@ -2350,7 +2402,7 @@ dns_rpz_delete(dns_rpz_zones_t *rpzs, dns_rpz_num_t rpz_num, 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: diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index b575af2f5ec..15a14116480 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -1419,11 +1419,15 @@ static cfg_type_t cfg_type_dnstapoutput = { * zone [ policy (given|disabled|passthru|drop|tcp-only| * nxdomain|nodata|cname ) ] * [ 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 @@ -1614,6 +1618,8 @@ static cfg_tuplefielddef_t rpz_zone_fields[] = { { "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 = { @@ -1635,6 +1641,16 @@ static cfg_tuplefielddef_t rpz_fields[] = { { "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 = { @@ -1851,6 +1867,14 @@ view_clauses[] = { { "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 }, diff --git a/lib/ns/query.c b/lib/ns/query.c index 4d6f6524f2c..931a1335432 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -715,6 +716,7 @@ query_reset(ns_client_t *client, isc_boolean_t everything) { 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; @@ -1285,8 +1287,7 @@ rpz_log_fail_helper(ns_client_t *client, int level, dns_name_t *p_name, { 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; @@ -1310,13 +1311,23 @@ rpz_log_fail_helper(ns_client_t *client, int level, dns_name_t *p_name, 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)); } @@ -2512,24 +2523,33 @@ rpz_st_clear(ns_client_t *client) { 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 @@ -2544,6 +2564,19 @@ rpz_get_zbits(ns_client_t *client, 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; @@ -2733,7 +2766,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type, 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); } @@ -2921,8 +2954,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype, 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); } /* @@ -3017,6 +3049,283 @@ rpz_save_p(dns_rpz_st_t *st, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type, 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. */ @@ -3041,6 +3350,15 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr, 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); @@ -3048,8 +3366,6 @@ rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr, 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); @@ -3319,7 +3635,8 @@ rpz_rewrite_ip_rrsets(ns_client_t *client, dns_name_t *name, 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; @@ -3335,15 +3652,27 @@ rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name, 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 @@ -3361,8 +3690,6 @@ rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name, 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. @@ -3513,6 +3840,15 @@ rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname, 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. */ @@ -3526,12 +3862,15 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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"); @@ -3543,7 +3882,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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)) { @@ -3560,6 +3899,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, if (st == NULL) return (ISC_R_NOMEMORY); st->state = 0; + st->rpsdb = NULL; } if (st->state == 0) { st->state |= DNS_RPZ_ACTIVE; @@ -3579,6 +3919,26 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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 + } } /* @@ -3588,7 +3948,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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: @@ -3598,7 +3958,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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: @@ -3608,19 +3968,19 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, * 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); @@ -3630,19 +3990,23 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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; } @@ -3671,9 +4035,12 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, * 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; @@ -3699,7 +4066,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, * 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; /* @@ -3710,13 +4077,14 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, } /* - * 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, @@ -3799,13 +4167,13 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t 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; } } @@ -3843,6 +4211,7 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, qtype, DNS_RPZ_TYPE_NSDNAME, DNS_RPZ_ALL_ZBITS, + ISC_TRUE, &rdataset); if (result != ISC_R_SUCCESS) { dns_rdata_freestruct(&ns); @@ -3879,6 +4248,17 @@ rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype, 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) @@ -5427,7 +5807,8 @@ query_resume(query_ctx_t *qctx) { 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)", @@ -5789,6 +6170,17 @@ query_checkrpz(query_ctx_t *qctx, isc_result_t result) { 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; @@ -7584,7 +7976,6 @@ query_nodata(query_ctx_t *qctx, isc_result_t result) { */ 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. @@ -7688,16 +8079,16 @@ query_sign_nodata(query_ctx_t *qctx) { } /* - * 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)); + } } /*