From: Colin Vidal Date: Tue, 27 May 2025 12:20:02 +0000 (+0200) Subject: add tests for per-zone hook instance support X-Git-Tag: v9.21.14~56^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8e44d62593af4857ae637b32b269939a78338f5;p=thirdparty%2Fbind9.git add tests for per-zone hook instance support add system tests covering the individual support of zone plugins. --- diff --git a/bin/tests/system/hooks/conf/bad-topviewlevel.conf b/bin/tests/system/hooks/conf/bad-topviewlevel.conf deleted file mode 100644 index b5b267adac8..00000000000 --- a/bin/tests/system/hooks/conf/bad-topviewlevel.conf +++ /dev/null @@ -1,4 +0,0 @@ -view foo { - plugin query "driver/.libs/test-syncplugin.so"; -}; -plugin query "driver/.libs/test-syncplugin.so"; diff --git a/bin/tests/system/hooks/conf/bad-topviewlevel.conf.j2 b/bin/tests/system/hooks/conf/bad-topviewlevel.conf.j2 new file mode 100644 index 00000000000..9e7338dbd76 --- /dev/null +++ b/bin/tests/system/hooks/conf/bad-topviewlevel.conf.j2 @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +view foo { + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; +}; +plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; diff --git a/bin/tests/system/hooks/conf/good-toplevel.conf b/bin/tests/system/hooks/conf/good-toplevel.conf deleted file mode 100644 index ebfd44a9cc3..00000000000 --- a/bin/tests/system/hooks/conf/good-toplevel.conf +++ /dev/null @@ -1 +0,0 @@ -plugin query "driver/.libs/test-syncplugin.so"; diff --git a/bin/tests/system/hooks/conf/good-toplevel.conf.j2 b/bin/tests/system/hooks/conf/good-toplevel.conf.j2 new file mode 100644 index 00000000000..3e416a6c6a4 --- /dev/null +++ b/bin/tests/system/hooks/conf/good-toplevel.conf.j2 @@ -0,0 +1,14 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; diff --git a/bin/tests/system/hooks/conf/good-viewlevel.conf b/bin/tests/system/hooks/conf/good-viewlevel.conf deleted file mode 100644 index 24668bd8c25..00000000000 --- a/bin/tests/system/hooks/conf/good-viewlevel.conf +++ /dev/null @@ -1,3 +0,0 @@ -view foo { - plugin query "driver/.libs/test-syncplugin.so"; -}; diff --git a/bin/tests/system/hooks/conf/good-viewlevel.conf.j2 b/bin/tests/system/hooks/conf/good-viewlevel.conf.j2 new file mode 100644 index 00000000000..7efc0a175de --- /dev/null +++ b/bin/tests/system/hooks/conf/good-viewlevel.conf.j2 @@ -0,0 +1,16 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +view foo { + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; +}; diff --git a/bin/tests/system/hooks/conf/good-viewzonelevel.conf b/bin/tests/system/hooks/conf/good-viewzonelevel.conf deleted file mode 100644 index 41d4d4ecee7..00000000000 --- a/bin/tests/system/hooks/conf/good-viewzonelevel.conf +++ /dev/null @@ -1,8 +0,0 @@ -view someview { - zone "foo.bar" { - plugin query "driver/.libs/test-syncplugin.so"; - type primary; - file "foo.bar.db"; - }; - plugin query "driver/.libs/test-syncplugin.so"; -}; diff --git a/bin/tests/system/hooks/conf/good-viewzonelevel.conf.j2 b/bin/tests/system/hooks/conf/good-viewzonelevel.conf.j2 new file mode 100644 index 00000000000..a1b0f2cf46e --- /dev/null +++ b/bin/tests/system/hooks/conf/good-viewzonelevel.conf.j2 @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +view someview { + zone "foo.bar" { + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; + type primary; + file "foo.bar.db"; + }; + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; +}; diff --git a/bin/tests/system/hooks/conf/good-zonelevel.conf b/bin/tests/system/hooks/conf/good-zonelevel.conf deleted file mode 100644 index d4e546bb364..00000000000 --- a/bin/tests/system/hooks/conf/good-zonelevel.conf +++ /dev/null @@ -1,5 +0,0 @@ -zone "foo.bar" { - plugin query "driver/.libs/test-syncplugin.so"; - type primary; - file "foo.bar.db"; -}; diff --git a/bin/tests/system/hooks/conf/good-zonelevel.conf.j2 b/bin/tests/system/hooks/conf/good-zonelevel.conf.j2 new file mode 100644 index 00000000000..133ef864d7b --- /dev/null +++ b/bin/tests/system/hooks/conf/good-zonelevel.conf.j2 @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone "foo.bar" { + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { rcode servfail; }; + type primary; + file "foo.bar.db"; +}; diff --git a/bin/tests/system/hooks/driver/test-syncplugin.c b/bin/tests/system/hooks/driver/test-syncplugin.c index 52fd1835fcc..1d082900b66 100644 --- a/bin/tests/system/hooks/driver/test-syncplugin.c +++ b/bin/tests/system/hooks/driver/test-syncplugin.c @@ -17,35 +17,148 @@ #include +typedef struct { + isc_mem_t *mctx; + uint8_t rcode; + + /* + * Plugin will bails out without altering the response if qname first + * label matches `firstlbl`. + */ + char *firstlbl; +} syncplugin_t; + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + static ns_hookresult_t -syncplugin_hook(void *arg, void *cbdata, isc_result_t *resp) { - UNUSED(arg); - UNUSED(cbdata); +syncplugin__hook(void *arg, void *cbdata, isc_result_t *resp) { + query_ctx_t *qctx = (query_ctx_t *)arg; + syncplugin_t *inst = cbdata; + UNUSED(resp); - return NS_HOOK_CONTINUE; + if (inst->firstlbl != NULL) { + const dns_name_t *qname = qctx->client->query.qname; + dns_label_t label; + + dns_name_getlabel(qname, 0, &label); + + /* + * +1 because the first label byte is the length of the label + * itself + */ + if (strncmp(inst->firstlbl, (char *)label.base + 1, + strlen(inst->firstlbl)) == 0) + { + return NS_HOOK_CONTINUE; + } + } + + qctx->client->message->rcode = inst->rcode; + *resp = ns_query_done(qctx); + return NS_HOOK_RETURN; +} + +static cfg_clausedef_t syncplugin__cfgclauses[] = { + { "rcode", &cfg_type_astring, 0 }, + { "firstlbl", &cfg_type_qstring, CFG_CLAUSEFLAG_OPTIONAL } +}; + +static cfg_clausedef_t *syncplugin__cfgparamsclausesets[] = { + syncplugin__cfgclauses, NULL +}; + +static cfg_type_t syncplugin__cfgparams = { + "syncplugin-params", cfg_parse_mapbody, cfg_print_mapbody, + cfg_doc_mapbody, &cfg_rep_map, syncplugin__cfgparamsclausesets +}; + +static isc_result_t +syncplugin__parse_rcode(const cfg_obj_t *syncplugincfg, uint8_t *rcode) { + isc_result_t result; + const cfg_obj_t *obj = NULL; + const char *rcodestr = NULL; + + result = cfg_map_get(syncplugincfg, "rcode", &obj); + if (result != ISC_R_SUCCESS) { + return result; + } + + rcodestr = obj->value.string.base; + + if (strcmp("servfail", rcodestr) == 0) { + *rcode = dns_rcode_servfail; + } else if (strcmp("notimp", rcodestr) == 0) { + *rcode = dns_rcode_notimp; + } else if (strcmp("noerror", rcodestr) == 0) { + *rcode = dns_rcode_noerror; + } else if (strcmp("notauth", rcodestr) == 0) { + *rcode = dns_rcode_notauth; + } else if (strcmp("notzone", rcodestr) == 0) { + *rcode = dns_rcode_notzone; + } else { + result = ISC_R_FAILURE; + } + + return result; } isc_result_t plugin_register(const char *parameters, const void *cfg, const char *cfgfile, unsigned long cfgline, isc_mem_t *mctx, void *actx, ns_hooktable_t *hooktable, void **instp) { + isc_result_t result; + cfg_parser_t *parser = NULL; + cfg_obj_t *syncplugincfg = NULL; + const cfg_obj_t *obj = NULL; + isc_buffer_t b; ns_hook_t hook; + syncplugin_t *inst = NULL; - UNUSED(parameters); UNUSED(cfg); - UNUSED(cfgfile); - UNUSED(cfgline); - UNUSED(mctx); UNUSED(actx); - UNUSED(hooktable); - UNUSED(instp); - hook = (ns_hook_t){ .action = syncplugin_hook, - .action_data = NULL }; - ns_hook_add(hooktable, mctx, NS_QUERY_NODATA_BEGIN, &hook); + inst = isc_mem_get(mctx, sizeof(*inst)); + *inst = (syncplugin_t){ .mctx = mctx }; + *instp = inst; - return ISC_R_SUCCESS; + CHECK(cfg_parser_create(mctx, &parser)); + + isc_buffer_constinit(&b, parameters, strlen(parameters)); + isc_buffer_add(&b, strlen(parameters)); + + CHECK(cfg_parse_buffer(parser, &b, cfgfile, cfgline, + &syncplugin__cfgparams, 0, &syncplugincfg)); + + CHECK(syncplugin__parse_rcode(syncplugincfg, &inst->rcode)); + + if (cfg_map_get(syncplugincfg, "firstlbl", &obj) == ISC_R_SUCCESS) { + const char *firstlbl = cfg_obj_asstring(obj); + size_t len = strlen(firstlbl) + 1; + + inst->firstlbl = isc_mem_allocate(mctx, len); + strncpy(inst->firstlbl, firstlbl, len); + } + + hook = (ns_hook_t){ .action = syncplugin__hook, .action_data = inst }; + ns_hook_add(hooktable, mctx, NS_QUERY_NXDOMAIN_BEGIN, &hook); + +cleanup: + if (syncplugincfg != NULL) { + cfg_obj_destroy(parser, &syncplugincfg); + } + + if (parser != NULL) { + cfg_parser_destroy(&parser); + } + + return result; } isc_result_t @@ -63,7 +176,15 @@ plugin_check(const char *parameters, const void *cfg, const char *cfgfile, void plugin_destroy(void **instp) { - UNUSED(instp); + syncplugin_t *inst = *instp; + isc_mem_t *mctx = inst->mctx; + + if (inst->firstlbl != NULL) { + isc_mem_free(mctx, inst->firstlbl); + } + + isc_mem_put(mctx, inst, sizeof(*inst)); + *instp = NULL; } int diff --git a/bin/tests/system/hooks/ns2/example.db b/bin/tests/system/hooks/ns2/example.db new file mode 100644 index 00000000000..20bf60480d5 --- /dev/null +++ b/bin/tests/system/hooks/ns2/example.db @@ -0,0 +1,21 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 120 +@ SOA ns.unsigned. hostmaster.ns.unsigned. ( 1 3600 1200 604800 60 ) +@ NS ns +@ MX 10 mx + +ns A 10.53.0.1 + AAAA fd92:7065:b8e:ffff::1 + +a A 1.1.1.1 +mx A 2.2.2.2 diff --git a/bin/tests/system/hooks/ns2/example4.com.db b/bin/tests/system/hooks/ns2/example4.com.db new file mode 100644 index 00000000000..20bf60480d5 --- /dev/null +++ b/bin/tests/system/hooks/ns2/example4.com.db @@ -0,0 +1,21 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 120 +@ SOA ns.unsigned. hostmaster.ns.unsigned. ( 1 3600 1200 604800 60 ) +@ NS ns +@ MX 10 mx + +ns A 10.53.0.1 + AAAA fd92:7065:b8e:ffff::1 + +a A 1.1.1.1 +mx A 2.2.2.2 diff --git a/bin/tests/system/hooks/ns2/named.conf.j2 b/bin/tests/system/hooks/ns2/named.conf.j2 new file mode 100644 index 00000000000..35431d89142 --- /dev/null +++ b/bin/tests/system/hooks/ns2/named.conf.j2 @@ -0,0 +1,77 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + recursion no; + dnssec-validation yes; + notify yes; + minimal-responses no; +}; + +trust-anchors { }; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { + rcode noerror; +}; + +zone "example.com" { + type primary; + file "example.db"; +}; + +zone "example2.com" { + type primary; + file "example.db"; + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { + rcode servfail; + }; +}; + +zone "example3.com" { + type primary; + file "example.db"; + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { + rcode notimp; + }; +}; + +template exampletmpl { + type primary; + file "$name.db"; + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { + rcode notauth; + firstlbl "skipfoo"; + }; +}; + +zone "example4.com" { + template exampletmpl; + plugin query "@TOP_BUILDDIR@/testlib-driver-syncplugin.so" { + rcode notzone; + }; +}; diff --git a/bin/tests/system/hooks/tests_hooks.py b/bin/tests/system/hooks/tests_hooks.py index 98c23bf216e..93953d1bdf0 100644 --- a/bin/tests/system/hooks/tests_hooks.py +++ b/bin/tests/system/hooks/tests_hooks.py @@ -9,8 +9,14 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. +import dns +import pytest import isctest +pytest.importorskip("dns") + +pytestmark = pytest.mark.extra_artifacts(["conf/*.conf"]) + def test_hooks(): msg = isctest.query.create("example.com.", "A") @@ -27,5 +33,47 @@ def test_hooks_noextension(ns1, templates): test_hooks() -def test_plugins_config(run_tests_sh): +def test_hooks_global_hook(): + msg = isctest.query.create("idontexists.example.com", "A") + res = isctest.query.udp(msg, "10.53.0.2") + isctest.check.rcode(res, dns.rcode.NOERROR) + + +def test_hooks_zone_hook1(): + msg = isctest.query.create("idonotexists.example2.com", "A") + res = isctest.query.udp(msg, "10.53.0.2") + isctest.check.rcode(res, dns.rcode.SERVFAIL) + + +def test_hooks_zone_hook2(): + msg = isctest.query.create("idonotexists.example3.com", "A") + res = isctest.query.udp(msg, "10.53.0.2") + isctest.check.rcode(res, dns.rcode.NOTIMP) + + +# ensure a plugin defined in a template is correcty registered to zone using +# this template +def test_hooks_zonetemplate1(): + msg = isctest.query.create("idonotexists.example4.com", "A") + res = isctest.query.udp(msg, "10.53.0.2") + isctest.check.rcode(res, dns.rcode.NOTAUTH) + + +# ensure plugin defined in zone are correctly registered when the zone also +# using a template with plugins (the plugin defined in the template is called +# first, but it bails out without doing anything because the first label is +# "skipfoo". So the plugin defined in the zone is then called, and return +# notzone) +def test_hooks_zonetemplate2(): + msg = isctest.query.create("skipfoo.example4.com", "A") + res = isctest.query.udp(msg, "10.53.0.2") + isctest.check.rcode(res, dns.rcode.NOTZONE) + + +def test_hooks_zone_rndc_reload(servers): + ns2 = servers["ns2"] + ns2.rndc("reload") + + +def test_hooks_config(run_tests_sh): run_tests_sh()