diff --git a/tests/include/tests/ns.h b/tests/include/tests/ns.h index 831ae80399..8d57c4e364 100644 --- a/tests/include/tests/ns.h +++ b/tests/include/tests/ns.h @@ -74,6 +74,12 @@ isc_result_t ns_test_serve_zone(const char *zonename, const char *filename, dns_view_t *view); +/*% + * Set a hooktable on the served zone + */ +void +ns_test_serve_zone_sethooktab(ns_hooktable_t *hooktab); + /*% * Release the zone loaded by ns_test_serve_zone(). */ diff --git a/tests/libtest/ns.c b/tests/libtest/ns.c index 9deba3bfdf..55117d1408 100644 --- a/tests/libtest/ns.c +++ b/tests/libtest/ns.c @@ -139,6 +139,11 @@ teardown_server(void **state) { static dns_zone_t *served_zone = NULL; +void +ns_test_serve_zone_sethooktab(ns_hooktable_t *hooktab) { + dns_zone_sethooktable(served_zone, hooktab, ns_hooktable_free); +} + isc_result_t ns_test_serve_zone(const char *zonename, const char *filename, dns_view_t *view) { diff --git a/tests/ns/query_test.c b/tests/ns/query_test.c index d2fa95e8cf..84f4f36704 100644 --- a/tests/ns/query_test.c +++ b/tests/ns/query_test.c @@ -1480,11 +1480,273 @@ ISC_LOOP_TEST_IMPL(ns__query_hookasync_e2e) { isc_loopmgr_shutdown(); } +/* + * Tests covering the correctness of hook call order, i.e. hooks from a zone are + * called first, then hooks from a view, then the default hook table. And any + * hook returning NS_HOOK_RETURN interrupt the whole chain + */ +typedef struct { + ns_hook_action_t zonehookactions[2]; + ns_hook_action_t viewhookactions[2]; + ns_hook_action_t defaulthookactions[2]; + const char *expected; +} ns__query_hook_test_params_t; + +static void +ns__query_test_concat(char *base, const char *tail) { + char b[512]; + + strcpy(b, base); + snprintf(base, sizeof(b), "%s%s", b, tail); +} + +static ns_hookresult_t +ns__query_test_zonehook1(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "z1"); + return NS_HOOK_CONTINUE; +} + +static ns_hookresult_t +ns__query_test_zonehook2(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "z2"); + return NS_HOOK_RETURN; +} + +static ns_hookresult_t +ns__query_test_viewhook1(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "v1"); + return NS_HOOK_CONTINUE; +} + +static ns_hookresult_t +ns__query_test_viewhook2(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "v2"); + return NS_HOOK_RETURN; +} + +static ns_hookresult_t +ns__query_test_defaulthook1(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "d1"); + return NS_HOOK_CONTINUE; +} + +static ns_hookresult_t +ns__query_test_defaulthook2(void *arg, void *data, isc_result_t *resultp) { + UNUSED(arg); + UNUSED(resultp); + + ns__query_test_concat(data, "d2"); + return NS_HOOK_RETURN; +} + +static bool +ns__query_test_setup_hooks(const ns_hook_t *h1, const ns_hook_t *h2, + ns_hooktable_t **tp) { + if (h1->action || h2->action) { + INSIST(*tp == NULL); + ns_hooktable_create(isc_g_mctx, tp); + + if (h1->action) { + ns_hook_add(*tp, isc_g_mctx, NS_QUERY_NXDOMAIN_BEGIN, + h1); + } + + if (h2->action) { + ns_hook_add(*tp, isc_g_mctx, NS_QUERY_NXDOMAIN_BEGIN, + h2); + } + + return true; + } + + return false; +} + +static void +ns__query_test_run_hookchain_test(const ns__query_hook_test_params_t *test) { + isc_result_t result; + query_ctx_t *qctx = NULL; + char buffer[512] = { 0 }; + ns_hooktable_t *zone_hooktab = NULL; + ns_hooktable_t *view_hooktab = NULL; + + const ns_test_qctx_create_params_t qctx_params = { + .qname = "idontexists.foo", + .qtype = dns_rdatatype_a, + .with_cache = true, + }; + + const ns_hook_t zonehook1 = { .action = test->zonehookactions[0], + .action_data = buffer }; + + const ns_hook_t zonehook2 = { .action = test->zonehookactions[1], + .action_data = buffer }; + + const ns_hook_t viewhook1 = { .action = test->viewhookactions[0], + .action_data = buffer }; + + const ns_hook_t viewhook2 = { .action = test->viewhookactions[1], + .action_data = buffer }; + + const ns_hook_t defaulthook1 = { .action = test->defaulthookactions[0], + .action_data = buffer }; + + const ns_hook_t defaulthook2 = { .action = test->defaulthookactions[1], + .action_data = buffer }; + + /* + * Create a fake query context + */ + result = ns_test_qctx_create(&qctx_params, &qctx); + INSIST(result == ISC_R_SUCCESS); + + /* + * Load a zone + */ + result = ns_test_serve_zone("foo", TESTS_DIR "/testdata/query/foo.db", + qctx->client->inner.view); + INSIST(result == ISC_R_SUCCESS); + + /* + * Attach hooks to the zone + */ + if (ns__query_test_setup_hooks(&zonehook1, &zonehook2, &zone_hooktab)) { + ns_test_serve_zone_sethooktab(zone_hooktab); + } + + /* + * Attach hooks to the view + */ + if (ns__query_test_setup_hooks(&viewhook1, &viewhook2, &view_hooktab)) { + qctx->client->inner.view->hooktable = view_hooktab; + } + + /* + * Setup the default hook table + */ + (void)ns__query_test_setup_hooks(&defaulthook1, &defaulthook2, + &ns__hook_table); + + /* + * Handling the response + */ + qctx->client->inner.sendcb = send_noop; + isc_nmhandle_attach(qctx->client->inner.handle, + &qctx->client->inner.reqhandle); + + /* + * Run the query + */ + ns__query_start(qctx); + ns_query_done(qctx); + + /* + * Result checking + */ + assert_string_equal(buffer, test->expected); + + /* + * Cleanup + */ + ns_test_qctx_destroy(&qctx); + ns_test_cleanup_zone(); + + if (ns__hook_table) { + ns_hooktable_free(isc_g_mctx, (void **)&ns__hook_table); + } + + if (view_hooktab) { + ns_hooktable_free(isc_g_mctx, (void **)&view_hooktab); + } +} + +ISC_LOOP_TEST_IMPL(ns__query_hookchain) { + const ns__query_hook_test_params_t tests[] = { + { { ns__query_test_zonehook1, ns__query_test_zonehook1 }, + { ns__query_test_viewhook1, ns__query_test_viewhook1 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z1z1v1v1d1d1" }, + { { ns__query_test_zonehook1, ns__query_test_zonehook1 }, + { ns__query_test_viewhook1, ns__query_test_viewhook1 }, + { ns__query_test_defaulthook2, ns__query_test_defaulthook1 }, + "z1z1v1v1d2" }, + { { ns__query_test_zonehook2, ns__query_test_zonehook1 }, + { ns__query_test_viewhook1, ns__query_test_viewhook1 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z2" }, + { { ns__query_test_zonehook1, ns__query_test_zonehook2 }, + { ns__query_test_viewhook1, ns__query_test_viewhook1 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z1z2" }, + { { ns__query_test_zonehook1, ns__query_test_zonehook1 }, + { ns__query_test_viewhook2, ns__query_test_viewhook1 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z1z1v2" }, + { { ns__query_test_zonehook1, ns__query_test_zonehook1 }, + { ns__query_test_viewhook1, ns__query_test_viewhook2 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z1z1v1v2" }, + { { ns__query_test_zonehook1, NULL }, + { ns__query_test_viewhook1, ns__query_test_viewhook2 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "z1v1v2" }, + { { NULL, NULL }, + { ns__query_test_viewhook1, ns__query_test_viewhook2 }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "v1v2" }, + { { NULL, NULL }, + { ns__query_test_viewhook1, NULL }, + { ns__query_test_defaulthook1, ns__query_test_defaulthook1 }, + "v1d1d1" }, + { { NULL, NULL }, + { ns__query_test_viewhook1, NULL }, + { NULL, NULL }, + "v1" }, + { { NULL, NULL }, + { ns__query_test_viewhook2, NULL }, + { NULL, NULL }, + "v2" }, + { { ns__query_test_zonehook1, NULL }, + { NULL, NULL }, + { NULL, NULL }, + "z1" }, + { { NULL, NULL }, + { NULL, NULL }, + { ns__query_test_defaulthook1, NULL }, + "d1" }, + { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, "" }, + + }; + + for (size_t i = 0; i < ARRAY_SIZE(tests); i++) { + ns__query_test_run_hookchain_test(&tests[i]); + } + + isc_loop_teardown(isc_loop_main(), shutdown_interfacemgr, NULL); + isc_loopmgr_shutdown(); +} + ISC_TEST_LIST_START ISC_TEST_ENTRY_CUSTOM(ns__query_sfcache, setup_server, teardown_server) ISC_TEST_ENTRY_CUSTOM(ns__query_start, setup_server, teardown_server) ISC_TEST_ENTRY_CUSTOM(ns__query_hookasync, setup_server, teardown_server) ISC_TEST_ENTRY_CUSTOM(ns__query_hookasync_e2e, setup_server, teardown_server) +ISC_TEST_ENTRY_CUSTOM(ns__query_hookchain, setup_server, teardown_server) ISC_TEST_LIST_END ISC_TEST_MAIN