]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add query unit test
authorColin Vidal <colin@isc.org>
Wed, 28 May 2025 09:54:19 +0000 (11:54 +0200)
committerColin Vidal <colin@isc.org>
Tue, 9 Sep 2025 07:42:34 +0000 (09:42 +0200)
A new query unit test covers the logic where zone hooks must be called
first, then view ones, and finally the default hooks. It also ensures
that if any hook returns NS_HOOK_RETURN the chain immediately stops.

tests/include/tests/ns.h
tests/libtest/ns.c
tests/ns/query_test.c

index 831ae8039966f821084839c4021f197ab217742a..8d57c4e36406b3837af5bdf199d690b28a31780d 100644 (file)
@@ -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().
  */
index 9deba3bfdf3c95254b229cab26a0bab7180b35fe..55117d1408d00484a4a658192af61ef1aff81813 100644 (file)
@@ -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) {
index d2fa95e8cf90437888d86547914ebc1e7913a8d4..84f4f36704ed4e50404e00bcc5387876c13af390 100644 (file)
@@ -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