bool old_rpz_ok = false;
isc_dscp_t dscp4 = -1, dscp6 = -1;
dns_dyndbctx_t *dctx = NULL;
+ ns_hookctx_t *hctx = NULL;
unsigned int resolver_param;
dns_ntatable_t *ntatable = NULL;
const char *qminmode = NULL;
CHECK(configure_dyndb(dyndb, mctx, dctx));
}
+
+ /*
+ * XXX:
+ * temporary! this forces loading of filter-aaaa.so from the
+ * current working directory, if present. later this will
+ * happen via configuration as dyndb does above. we don't
+ * bother checking whether it succeeded; if it doesn't,
+ * filter-aaaa simply won't work.
+ */
+ if (hctx == NULL) {
+ CHECK(ns_hook_createctx(mctx, &hctx));
+ }
+ ns_hooktable_init(NULL);
+ (void) ns_hookmodule_load("/tmp/filter-aaaa.so", "", "<none>", 0,
+ hctx, NULL);
#endif
/*
if (dctx != NULL) {
dns_dyndb_destroyctx(&dctx);
}
+ if (hctx != NULL) {
+ ns_hook_destroyctx(&hctx);
+ }
return (result);
}
CHECK(cfg_aclconfctx_create(named_g_mctx, &named_g_aclconfctx));
/*
- * Shut down all dyndb instances.
+ * Shut down all dyndb and hook module instances.
*/
dns_dyndb_cleanup(false);
ns_hookmodule_cleanup();
dns_view_detach(&view);
}
+ /*
+ * Shut down all dyndb and hook module instances.
+ */
dns_dyndb_cleanup(true);
ns_hookmodule_cleanup();
case $host in #(
*-linux*|*-gnu*) :
+ LDFLAGS="${LDFLAGS} -Wl,--export-dynamic"
SO_CFLAGS="-fPIC"
- LDFLAGS="${LDFLAGS} -Wl,--export-dynamic"
SO_LDFLAGS=""
if test "$use_libtool" = "yes"; then :
AS_IF([test "$with_dlopen" = "yes"],
[AS_CASE([$host],
[*-linux*|*-gnu*],[
+ LDFLAGS="${LDFLAGS} -Wl,--export-dynamic"
SO_CFLAGS="-fPIC"
- LDFLAGS="${LDFLAGS} -Wl,--export-dynamic"
SO_LDFLAGS=""
AS_IF([test "$use_libtool" = "yes"],[
SO_LDFLAGS="-Xcompiler -shared"
update.c version.c xfrout.c
SUBDIRS = include
-TARGETS = timestamp
TESTDIRS = @UNITTESTS@
+SO_TARGETS = filter-aaaa.@SO@
+TARGETS = timestamp @SO_TARGETS@
+
+SO_CFLAGS = @CFLAGS@ @SO_CFLAGS@
+SO_LDFLAGS = @LDFLAGS@ @SO_LDFLAGS@
@BIND9_MAKE_RULES@
timestamp: libns.@A@
touch timestamp
+filter-aaaa.@O@: filter-aaaa.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} ${SO_CFLAGS} \
+ -c ${srcdir}/filter-aaaa.c
+
+filter-aaaa.@SO@: filter-aaaa.o libns.@A@
+ ${LIBTOOL_MODE_LINK} @SO_LD@ ${SO_LDFLAGS} -o $@ filter-aaaa.o \
+ ${ISCLIBS} ${DNSLIBS} libns.@A@ ${ISCLIBS} @DNS_CRYPTO_LIBS@ ${DNSLIBS} ${LIBS}
+
installdirs:
$(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir}
${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${libdir}/libns.@A@
clean distclean::
- rm -f libns.@A@ timestamp
+ rm -f libns.@A@ timestamp filter-aaaa.@O@ filter-aaaa.@SO@
--- /dev/null
+/*
+ * Copyright (C) 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/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/hash.h>
+#include <isc/lib.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <ns/client.h>
+#include <ns/hooks.h>
+#include <ns/log.h>
+#include <ns/query.h>
+
+ns_hook_destroy_t hook_destroy;
+ns_hook_register_t hook_register;
+ns_hook_version_t hook_version;
+
+/*
+ * Per-client flags set by this module
+ */
+#define FILTER_AAAA_RECURSING 0x0001 /* Recursing for A */
+#define FILTER_AAAA_FILTERED 0x0002 /* AAAA was removed from answer */
+
+
+/*% Want DNSSEC? */
+#define WANTDNSSEC(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTDNSSEC) != 0)
+/*% Recursion OK? */
+#define RECURSIONOK(c) (((c)->query.attributes & \
+ NS_QUERYATTR_RECURSIONOK) != 0)
+
+static bool
+filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp);
+
+ns_hook_t filter_respbegin = {
+ .callback = filter_respond_begin,
+};
+ns_hook_t filter_respanyfound = {
+ .callback = filter_respond_any_found,
+};
+ns_hook_t filter_prepresp = {
+ .callback = filter_prep_response_begin,
+};
+ns_hook_t filter_donesend = {
+ .callback = filter_query_done_send,
+};
+
+isc_result_t
+hook_register(const char *parameters, const char *file, unsigned long line,
+ ns_hookctx_t *hctx, ns_hooktable_t *hooktable, void **instp)
+{
+ UNUSED(parameters);
+ UNUSED(instp);
+
+ isc_log_write(hctx->lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
+ "loading params for 'filter-aaaa' module from %s:%lu",
+ file, line);
+
+ /*
+ * TODO:
+ * configure with parameters here
+ */
+
+ ns_hook_add(hooktable, NS_QUERY_RESPOND_BEGIN,
+ &filter_respbegin);
+ ns_hook_add(hooktable, NS_QUERY_RESPOND_ANY_FOUND,
+ &filter_respanyfound);
+ ns_hook_add(hooktable, NS_QUERY_PREP_RESPONSE_BEGIN,
+ &filter_prepresp);
+ ns_hook_add(hooktable, NS_QUERY_DONE_SEND,
+ &filter_donesend);
+
+ /*
+ * TODO:
+ * Set up a serial number that can be used for accessing
+ * data blobs in qctx, client, view;
+ * return an instance pointer for later destruction
+ */
+ return (ISC_R_SUCCESS);
+}
+
+void
+hook_destroy(void **instp) {
+ UNUSED(instp);
+
+ return;
+}
+
+int
+hook_version(unsigned int *flags) {
+ UNUSED(flags);
+
+ return (NS_HOOK_VERSION);
+}
+
+/*
+ * Check whether this is a V4 client.
+ */
+static bool
+is_v4_client(ns_client_t *client) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET) {
+ return (true);
+ }
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
+ {
+ return (true);
+ }
+ return (false);
+}
+
+/*
+ * Check whether this is a V6 client.
+ */
+static bool
+is_v6_client(ns_client_t *client) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
+ !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
+ {
+ return (true);
+ }
+ return (false);
+}
+
+/*
+ * The filter-aaaa-on-v4 option suppresses AAAAs for IPv4
+ * clients if there is an A; filter-aaaa-on-v6 option does
+ * the same for IPv6 clients.
+ */
+static bool
+filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ isc_result_t result;
+
+ UNUSED(cbdata);
+
+ qctx->filter_aaaa = dns_aaaa_ok;
+ if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
+ qctx->client->view->v6_aaaa != dns_aaaa_ok)
+ {
+ result = ns_client_checkaclsilent(qctx->client, NULL,
+ qctx->client->view->aaaa_acl,
+ true);
+ if (result == ISC_R_SUCCESS &&
+ qctx->client->view->v4_aaaa != dns_aaaa_ok &&
+ is_v4_client(qctx->client))
+ {
+ qctx->filter_aaaa = qctx->client->view->v4_aaaa;
+ } else if (result == ISC_R_SUCCESS &&
+ qctx->client->view->v6_aaaa != dns_aaaa_ok &&
+ is_v6_client(qctx->client))
+ {
+ qctx->filter_aaaa = qctx->client->view->v6_aaaa;
+ }
+ }
+
+ *resp = ISC_R_UNSET;
+ return (false);
+}
+
+/*
+ * Optionally hide AAAA rrsets if there is a matching A.
+ * (This version is for processing answers to explicit AAAA
+ * queries; ANY queries are handled in query_filter_aaaa_any().)
+ */
+static bool
+filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ isc_result_t result = ISC_R_UNSET;
+
+ UNUSED(cbdata);
+
+ if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
+ (qctx->filter_aaaa != dns_aaaa_filter ||
+ (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
+ dns_rdataset_isassociated(qctx->sigrdataset))))
+ {
+ *resp = result;
+ return (false);
+ }
+
+ if (qctx->qtype == dns_rdatatype_aaaa) {
+ dns_rdataset_t *trdataset;
+ trdataset = ns_client_newrdataset(qctx->client);
+ result = dns_db_findrdataset(qctx->db, qctx->node,
+ qctx->version,
+ dns_rdatatype_a, 0,
+ qctx->client->now,
+ trdataset, NULL);
+ if (dns_rdataset_isassociated(trdataset)) {
+ dns_rdataset_disassociate(trdataset);
+ }
+ ns_client_putrdataset(qctx->client, &trdataset);
+
+ /*
+ * We found an AAAA. If we also found an A, then the AAAA
+ * must not be rendered.
+ *
+ * If the A is not in our cache, then any result other than
+ * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A,
+ * and so AAAAs are okay.
+ *
+ * We assume there is no A if we can't recurse for this
+ * client. That might be the wrong answer, but what else
+ * can we do? Besides, the fact that we have the AAAA and
+ * are using this mechanism in the first place suggests
+ * that we care more about As than AAAAs, and would have
+ * cached an A if it existed.
+ */
+ if (result == ISC_R_SUCCESS) {
+ qctx->rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+ if (qctx->sigrdataset != NULL &&
+ dns_rdataset_isassociated(qctx->sigrdataset))
+ {
+ qctx->sigrdataset->attributes |=
+ DNS_RDATASETATTR_RENDERED;
+ }
+ qctx->client->hookflags |= FILTER_AAAA_FILTERED;
+ } else if (!qctx->authoritative &&
+ RECURSIONOK(qctx->client) &&
+ (result == DNS_R_DELEGATION ||
+ result == ISC_R_NOTFOUND))
+ {
+ /*
+ * This is an ugly kludge to recurse
+ * for the A and discard the result.
+ *
+ * Continue to add the AAAA now.
+ * We'll make a note to not render it
+ * if the recursion for the A succeeds.
+ */
+ result = ns_query_recurse(qctx->client,
+ dns_rdatatype_a,
+ qctx->client->query.qname,
+ NULL, NULL, qctx->resuming);
+ if (result == ISC_R_SUCCESS) {
+ qctx->client->hookflags |=
+ FILTER_AAAA_RECURSING;
+ qctx->client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ }
+ }
+ } else if (qctx->qtype == dns_rdatatype_a &&
+ ((qctx->client->hookflags & FILTER_AAAA_RECURSING) != 0))
+ {
+
+ dns_rdataset_t *mrdataset = NULL;
+ dns_rdataset_t *sigrdataset = NULL;
+
+ result = dns_message_findname(qctx->client->message,
+ DNS_SECTION_ANSWER, qctx->fname,
+ dns_rdatatype_aaaa, 0,
+ NULL, &mrdataset);
+ if (result == ISC_R_SUCCESS) {
+ mrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+
+ result = dns_message_findname(qctx->client->message,
+ DNS_SECTION_ANSWER, qctx->fname,
+ dns_rdatatype_rrsig,
+ dns_rdatatype_aaaa,
+ NULL, &sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+
+ qctx->client->hookflags &= ~FILTER_AAAA_RECURSING;
+
+ result = ns_query_done(qctx);
+
+ *resp = result;
+ return (true);
+
+ }
+
+ *resp = result;
+ return (false);
+}
+
+static bool
+filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ dns_name_t *name = NULL;
+ dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
+ dns_rdataset_t *a = NULL;
+ bool have_a = true;
+
+ UNUSED(cbdata);
+
+ if (qctx->filter_aaaa == dns_aaaa_ok) {
+ *resp = ISC_R_UNSET;
+ return (false);
+ }
+
+ dns_message_findname(qctx->client->message, DNS_SECTION_ANSWER,
+ (qctx->fname != NULL)
+ ? qctx->fname
+ : qctx->tname,
+ dns_rdatatype_any, 0, &name, NULL);
+
+ /*
+ * If we're not authoritative, just assume there's an
+ * A even if it wasn't in the cache and therefore isn't
+ * in the message. But if we're authoritative, then
+ * if there was an A, it should be here.
+ */
+ if (qctx->authoritative && name != NULL) {
+ dns_message_findtype(name, dns_rdatatype_a, 0, &a);
+ if (a == NULL) {
+ have_a = false;
+ }
+ }
+
+ if (name != NULL) {
+ dns_message_findtype(name, dns_rdatatype_aaaa, 0, &aaaa);
+ dns_message_findtype(name, dns_rdatatype_rrsig,
+ dns_rdatatype_aaaa, &aaaa_sig);
+ }
+
+ if (have_a && aaaa != NULL &&
+ (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
+ qctx->filter_aaaa == dns_aaaa_break_dnssec))
+ {
+ aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
+ if (aaaa_sig != NULL) {
+ aaaa_sig->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+ }
+
+ *resp = ISC_R_UNSET;
+ return (false);
+}
+
+/*
+ * Hide AAAA rrsets in the additional section if there is a matching A,
+ * and hide NS in the additional section if AAAA was filtered in the answer
+ * section.
+ */
+static bool
+filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ isc_result_t result;
+
+ UNUSED(cbdata);
+
+ if (qctx->filter_aaaa == dns_aaaa_ok) {
+ *resp = ISC_R_UNSET;
+ return (false);
+ }
+
+ result = dns_message_firstname(qctx->client->message,
+ DNS_SECTION_ADDITIONAL);
+ while (result == ISC_R_SUCCESS) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
+ dns_rdataset_t *a = NULL;
+
+ dns_message_currentname(qctx->client->message,
+ DNS_SECTION_ADDITIONAL,
+ &name);
+
+ result = dns_message_nextname(qctx->client->message,
+ DNS_SECTION_ADDITIONAL);
+
+ dns_message_findtype(name, dns_rdatatype_a, 0, &a);
+ if (a == NULL) {
+ continue;
+ }
+
+ dns_message_findtype(name, dns_rdatatype_aaaa, 0,
+ &aaaa);
+ if (aaaa == NULL) {
+ continue;
+ }
+
+ dns_message_findtype(name, dns_rdatatype_rrsig,
+ dns_rdatatype_aaaa, &aaaa_sig);
+
+ if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
+ qctx->filter_aaaa == dns_aaaa_break_dnssec)
+ {
+ aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
+ if (aaaa_sig != NULL) {
+ aaaa_sig->attributes |=
+ DNS_RDATASETATTR_RENDERED;
+ }
+ }
+ }
+
+ if ((qctx->client->hookflags & FILTER_AAAA_FILTERED) != 0) {
+ result = dns_message_firstname(qctx->client->message,
+ DNS_SECTION_AUTHORITY);
+ while (result == ISC_R_SUCCESS) {
+ dns_name_t *name = NULL;
+ dns_rdataset_t *ns = NULL, *ns_sig = NULL;
+
+ dns_message_currentname(qctx->client->message,
+ DNS_SECTION_AUTHORITY,
+ &name);
+
+ result = dns_message_findtype(name, dns_rdatatype_ns,
+ 0, &ns);
+ if (result == ISC_R_SUCCESS) {
+ ns->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+
+ result = dns_message_findtype(name, dns_rdatatype_rrsig,
+ dns_rdatatype_ns,
+ &ns_sig);
+ if (result == ISC_R_SUCCESS) {
+ ns_sig->attributes |= DNS_RDATASETATTR_RENDERED;
+ }
+
+ result = dns_message_nextname(qctx->client->message,
+ DNS_SECTION_AUTHORITY);
+ }
+ }
+
+ *resp = ISC_R_UNSET;
+ return (false);
+}
char *filename;
ns_hook_register_t *register_func;
ns_hook_destroy_t *destroy_func;
- char *name;
void *inst;
LINK(ns_hook_module_t) link;
};
REQUIRE(hmodp != NULL && *hmodp == NULL);
- isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
- NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
- "loading module '%s'",
- filename);
-
flags = RTLD_NOW|RTLD_LOCAL;
#ifdef RTLD_DEEPBIND
flags |= RTLD_DEEPBIND;
CHECK(ISC_R_FAILURE);
}
- CHECK(load_symbol(handle, filename, "hook_init",
+ CHECK(load_symbol(handle, filename, "hook_register",
(void **)®ister_func));
CHECK(load_symbol(handle, filename, "hook_destroy",
(void **)&destroy_func));
hmod = isc_mem_get(mctx, sizeof(*hmod));
- if (hmod == NULL) {
- CHECK(ISC_R_NOMEMORY);
- }
-
hmod->mctx = NULL;
isc_mem_attach(mctx, &hmod->mctx);
hmod->handle = handle;
isc_mem_free(hmod->mctx, hmod->filename);
}
- isc_mem_putanddetach(&hmod->mctx, hmod, sizeof(ns_hook_module_t));
+ isc_mem_putanddetach(&hmod->mctx, hmod, sizeof(*hmod));
}
#elif _WIN32
static isc_result_t
REQUIRE(hmodp != NULL && *hmodp == NULL);
- isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
- NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
- "loading module '%s'", filename);
-
handle = LoadLibraryA(filename);
if (handle == NULL) {
CHECK(ISC_R_FAILURE);
CHECK(ISC_R_FAILURE);
}
- CHECK(load_symbol(handle, filename, "hook_init",
+ CHECK(load_symbol(handle, filename, "hook_register",
(void **)®ister_func));
CHECK(load_symbol(handle, filename, "hook_destroy",
(void **)&destroy_func));
hmod = isc_mem_get(mctx, sizeof(*hmod));
- if (hmod == NULL) {
- CHECK(ISC_R_NOMEMORY);
- }
-
hmod->mctx = NULL;
isc_mem_attach(mctx, &hmod->mctx);
hmod->handle = handle;
isc_result_t
ns_hookmodule_load(const char *libname, const char *parameters,
- const char *file, unsigned long line, isc_mem_t *mctx)
+ const char *file, unsigned long line,
+ ns_hookctx_t *hctx, ns_hooktable_t *hooktable)
{
isc_result_t result;
ns_hook_module_t *module = NULL;
- RUNTIME_CHECK(isc_once_do(&once, init_modules) == ISC_R_SUCCESS);
+ if (hooktable == NULL) {
+ hooktable = ns__hook_table;
+ }
+
+ REQUIRE(NS_HOOKCTX_VALID(hctx));
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
"loading module '%s'", libname);
- CHECK(load_library(mctx, libname, &module));
- CHECK(module->register_func(mctx, parameters, file, line,
- &module->inst));
-
+ CHECK(load_library(hctx->mctx, libname, &module));
+ CHECK(module->register_func(parameters, file, line, hctx,
+ hooktable, &module->inst));
APPEND(hook_modules, module, link);
result = ISC_R_SUCCESS;
UNLINK(hook_modules, hmod, link);
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
- "unloading module '%s'", hmod->name);
+ "unloading module '%s'", hmod->filename);
hmod->destroy_func(&hmod->inst);
ENSURE(hmod->inst == NULL);
unload_library(&hmod);
}
}
+isc_result_t
+ns_hook_createctx(isc_mem_t *mctx, ns_hookctx_t **hctxp) {
+ ns_hookctx_t *hctx;
+
+ REQUIRE(hctxp != NULL && *hctxp == NULL);
+
+ hctx = isc_mem_get(mctx, sizeof(*hctx));
+ memset(hctx, 0, sizeof(*hctx));
+ hctx->lctx = ns_lctx;
+
+ isc_mem_attach(mctx, &hctx->mctx);
+ hctx->magic = NS_HOOKCTX_MAGIC;
+
+ *hctxp = hctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_hook_destroyctx(ns_hookctx_t **hctxp) {
+ ns_hookctx_t *hctx;
+
+ REQUIRE(hctxp != NULL && NS_HOOKCTX_VALID(*hctxp));
+
+ hctx = *hctxp;
+ *hctxp = NULL;
+
+ hctx->magic = 0;
+
+ hctx->lctx = NULL;
+
+ isc_mem_putanddetach(&hctx->mctx, hctx, sizeof(*hctx));
+}
+
void
ns_hooktable_init(ns_hooktable_t *hooktable) {
int i;
#include <stdbool.h>
#include <isc/list.h>
+#include <isc/magic.h>
#include <isc/result.h>
+#include <dns/rdatatype.h>
+
+#include <ns/client.h>
+#include <ns/query.h>
/*
* Hooks provide a way of running a callback function once a certain place in
* code is reached. Current use is limited to libns unit tests and thus:
typedef bool
(*ns_hook_cb_t)(void *hook_data, void *callback_data, isc_result_t *resultp);
+typedef struct ns_hook {
+ ns_hook_cb_t callback;
+ void *callback_data;
+ ISC_LINK(struct ns_hook) link;
+} ns_hook_t;
+
+/*
+ * ns__hook_table is a globally visible pointer to the active hook
+ * table. It's initialized to point to 'hooktab', which is the default
+ * global hook table.
+ */
+typedef ISC_LIST(ns_hook_t) ns_hooklist_t;
+typedef ns_hooklist_t ns_hooktable_t[NS_QUERY_HOOKS_COUNT];
+LIBNS_EXTERNAL_DATA extern ns_hooktable_t *ns__hook_table;
+
+/*!
+ * Context for intializing a hook module.
+ *
+ * This structure passes data to which a hook module will need
+ * access -- server memory context, hash initializer, log context, etc.
+ * The structure doesn't persist beyond configuring the hook module.
+ * The module's register function should attach to all reference-counted
+ * variables and its destroy function should detach from them.
+ */
+typedef struct ns_hookctx {
+ unsigned int magic;
+ isc_mem_t *mctx;
+ isc_log_t *lctx;
+} ns_hookctx_t;
+
+#define NS_HOOKCTX_MAGIC ISC_MAGIC('H', 'k', 'c', 'x')
+#define NS_HOOKCTX_VALID(d) ISC_MAGIC_VALID(d, NS_HOOKCTX_MAGIC)
/*
* API version
*
#define NS_HOOK_AGE 0
#endif
-typedef isc_result_t ns_hook_register_t(isc_mem_t *mctx,
- const char *parameters,
+typedef isc_result_t ns_hook_register_t(const char *parameters,
const char *file,
unsigned long line,
+ ns_hookctx_t *hctx,
+ ns_hooktable_t *hooktable,
void **instp);
/*%
* Called when registering a new module.
* the future to pass back driver capabilities or other information.
*/
-typedef struct ns_hook {
- ns_hook_cb_t callback;
- void *callback_data;
- ISC_LINK(struct ns_hook) link;
-} ns_hook_t;
-
-/*
- * ns__hook_table is a globally visible pointer to the active hook
- * table. It's initialized to point to 'hooktab', which is the default
- * global hook table.
- */
-typedef ISC_LIST(ns_hook_t) ns_hooklist_t;
-typedef ns_hooklist_t ns_hooktable_t[NS_QUERY_HOOKS_COUNT];
-LIBNS_EXTERNAL_DATA extern ns_hooktable_t *ns__hook_table;
-
/*
* Run a hook. Calls the function or functions registered at hookpoint 'id'.
* If one of them returns true, we interrupt processing and return the
#define NS_PROCESS_HOOK_VOID(table, id, data) \
_NS_PROCESS_HOOK(table, id, data)
+isc_result_t
+ns_hook_createctx(isc_mem_t *mctx, ns_hookctx_t **hctxp);
+
+void
+ns_hook_destroyctx(ns_hookctx_t **hctxp);
+
isc_result_t
ns_hookmodule_load(const char *libname, const char *parameters,
- const char *file, unsigned long line, isc_mem_t *mctx);
+ const char *file, unsigned long line,
+ ns_hookctx_t *hctx, ns_hooktable_t *hooktable);
void
ns_hookmodule_cleanup(void);
bool root_key_sentinel_not_ta;
};
-#define NS_QUERYATTR_RECURSIONOK 0x0001
-#define NS_QUERYATTR_CACHEOK 0x0002
-#define NS_QUERYATTR_PARTIALANSWER 0x0004
-#define NS_QUERYATTR_NAMEBUFUSED 0x0008
-#define NS_QUERYATTR_RECURSING 0x0010
-#define NS_QUERYATTR_QUERYOKVALID 0x0040
-#define NS_QUERYATTR_QUERYOK 0x0080
-#define NS_QUERYATTR_WANTRECURSION 0x0100
-#define NS_QUERYATTR_SECURE 0x0200
-#define NS_QUERYATTR_NOAUTHORITY 0x0400
-#define NS_QUERYATTR_NOADDITIONAL 0x0800
-#define NS_QUERYATTR_CACHEACLOKVALID 0x1000
-#define NS_QUERYATTR_CACHEACLOK 0x2000
-#define NS_QUERYATTR_DNS64 0x4000
-#define NS_QUERYATTR_DNS64EXCLUDE 0x8000
+#define NS_QUERYATTR_RECURSIONOK 0x00001
+#define NS_QUERYATTR_CACHEOK 0x00002
+#define NS_QUERYATTR_PARTIALANSWER 0x00004
+#define NS_QUERYATTR_NAMEBUFUSED 0x00008
+#define NS_QUERYATTR_RECURSING 0x00010
+#define NS_QUERYATTR_QUERYOKVALID 0x00040
+#define NS_QUERYATTR_QUERYOK 0x00080
+#define NS_QUERYATTR_WANTRECURSION 0x00100
+#define NS_QUERYATTR_SECURE 0x00200
+#define NS_QUERYATTR_NOAUTHORITY 0x00400
+#define NS_QUERYATTR_NOADDITIONAL 0x00800
+#define NS_QUERYATTR_CACHEACLOKVALID 0x01000
+#define NS_QUERYATTR_CACHEACLOK 0x02000
+#define NS_QUERYATTR_DNS64 0x04000
+#define NS_QUERYATTR_DNS64EXCLUDE 0x08000
#define NS_QUERYATTR_RRL_CHECKED 0x10000
#define NS_QUERYATTR_REDIRECT 0x20000
-/* query context structure */
+typedef struct query_ctx query_ctx_t;
-typedef struct query_ctx {
+/* query context structure */
+struct query_ctx {
isc_buffer_t *dbuf; /* name buffer */
dns_name_t *fname; /* found name from DB lookup */
dns_name_t *tname; /* temporary name, used
isc_result_t result; /* query result */
int line; /* line to report error */
-} query_ctx_t;
+};
/*
* The following functions are expected to be used only within query.c
void
ns_query_cancel(ns_client_t *client);
-/*%
- * (Must not be used outside this module and its associated unit tests.)
+/*
+ * The following functions are expected to be used only within query.c
+ * and query modules.
*/
+
isc_result_t
-ns__query_sfcache(query_ctx_t *qctx);
+ns_query_done(query_ctx_t *qctx);
+/*%<
+ * Finalize this phase of the query process:
+ *
+ * - Clean up.
+ * - If we have an answer ready (positive or negative), send it.
+ * - If we need to restart for a chaining query, call ns__query_start() again.
+ * - If we've started recursion, then just clean up; things will be
+ * restarted via fetch_callback()/query_resume().
+ */
+
+isc_result_t
+ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+ dns_name_t *qdomain, dns_rdataset_t *nameservers,
+ bool resuming);
+/*%<
+ * Prepare client for recursion, then create a resolver fetch, with
+ * the event callback set to fetch_callback(). Afterward we terminate
+ * this phase of the query, and resume with a new query context when
+ * recursion completes.
+ */
+
+isc_result_t
+ns__query_sfcache(query_ctx_t *qctx);
/*%
* (Must not be used outside this module and its associated unit tests.)
*/
+
isc_result_t
ns__query_start(query_ctx_t *qctx);
+/*%
+ * (Must not be used outside this module and its associated unit tests.)
+ */
+void
+ns__query_inithooks(void);
/*
* XXX:
* Temporary function used to initialize the filter-aaaa hooks,
* which are currently hard-coded rather than loaded as a module.
*/
-void
-ns__query_inithooks(void);
#endif /* NS_QUERY_H */
static void
query_addauth(query_ctx_t *qctx);
-/*
- * XXX:
- * This is a temporary hooks table, pre-populated with functions
- * implementing filter-aaaa. Later, this will be redesigned to be set up at
- * initialization time when the filter-aaaa module is loaded.
- *
- * To activate this hooks table at runtime, call ns__query_inithooks().
- */
-static bool
-filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp);
-
-static bool
-filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp);
-
-static bool
-filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp);
-
-static bool
-filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp);
-
-ns_hook_t filter_respbegin = {
- .callback = filter_respond_begin,
- .callback_data = NULL,
- .link = { (void *) -1, (void *) -1 },
-};
-ns_hook_t filter_respanyfound = {
- .callback = filter_respond_any_found,
- .callback_data = NULL,
- .link = { (void *) -1, (void *) -1 },
-};
-ns_hook_t filter_prepresp = {
- .callback = filter_prep_response_begin,
- .callback_data = NULL,
- .link = { (void *) -1, (void *) -1 },
-};
-ns_hook_t filter_donesend = {
- .callback = filter_query_done_send,
- .callback_data = NULL,
- .link = { (void *) -1, (void *) -1 },
-};
-
/*%
* Increment query statistics counters.
*/
return;
}
-static bool
-is_v4_client(ns_client_t *client) {
- if (isc_sockaddr_pf(&client->peeraddr) == AF_INET)
- return (true);
- if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
- IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
- return (true);
- return (false);
-}
-
-static bool
-is_v6_client(ns_client_t *client) {
- if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
- !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
- return (true);
- return (false);
-}
-
static uint32_t
dns64_ttl(dns_db_t *db, dns_dbversion_t *version) {
dns_dbnode_t *node = NULL;
}
}
-/*%
- * Prepare client for recursion, then create a resolver fetch, with
- * the event callback set to fetch_callback(). Afterward we terminate
- * this phase of the query, and resume with a new query context when
- * recursion completes.
- */
isc_result_t
ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
dns_name_t *qdomain, dns_rdataset_t *nameservers,
!dns_name_equal(qctx->client->query.qname, dns_rootname))
{
result = query_checkrpz(qctx, result);
- if (result == ISC_R_COMPLETE)
+ if (result == ISC_R_COMPLETE) {
return (ns_query_done(qctx));
+ }
}
/*
CCTRACE(ISC_LOG_ERROR,
"query_respond_any: rdataset iterator failed");
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
+ return (ns_query_done(qctx));
}
if (found) {
if (dns_rdatatype_atparent(qctx->type)) {
/*
* Parent is recursive for this rdata
- * type (i.e., DS)
+ * type (i.e., DS).
*/
result = ns_query_recurse(qctx->client,
qctx->qtype, qname,
} else if (qctx->dns64) {
/*
* Look up an A record so we can
- * synthesize DNS64
+ * synthesize DNS64.
*/
result = ns_query_recurse(qctx->client,
dns_rdatatype_a, qname,
qctx->resuming);
} else {
/*
- * Any other recursion
+ * Any other recursion.
*/
result = ns_query_recurse(qctx->client,
qctx->qtype, qname,
ns_client_attach(client, &qclient);
(void)query_setup(qclient, qtype);
}
-
-/*
- * Per-client flags set by this module
- */
-#define FILTER_AAAA_RECURSING 0x0001 /* Recursing for A */
-#define FILTER_AAAA_FILTERED 0x0002 /* AAAA was removed from answer */
-
-/*
- * The filter-aaaa-on-v4 option suppresses AAAAs for IPv4
- * clients if there is an A; filter-aaaa-on-v6 option does
- * the same for IPv6 clients.
- */
-static bool
-filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
- query_ctx_t *qctx = (query_ctx_t *) hookdata;
- isc_result_t result;
-
- UNUSED(cbdata);
-
- qctx->filter_aaaa = dns_aaaa_ok;
- if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
- qctx->client->view->v6_aaaa != dns_aaaa_ok)
- {
- result = ns_client_checkaclsilent(qctx->client, NULL,
- qctx->client->view->aaaa_acl,
- true);
- if (result == ISC_R_SUCCESS &&
- qctx->client->view->v4_aaaa != dns_aaaa_ok &&
- is_v4_client(qctx->client))
- {
- qctx->filter_aaaa = qctx->client->view->v4_aaaa;
- } else if (result == ISC_R_SUCCESS &&
- qctx->client->view->v6_aaaa != dns_aaaa_ok &&
- is_v6_client(qctx->client))
- {
- qctx->filter_aaaa = qctx->client->view->v6_aaaa;
- }
- }
-
- *resp = ISC_R_UNSET;
- return (false);
-}
-
-/*
- * Optionally hide AAAA rrsets if there is a matching A.
- * (This version is for processing answers to explicit AAAA
- * queries; ANY queries are handled in query_filter_aaaa_any().)
- */
-static bool
-filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
- query_ctx_t *qctx = (query_ctx_t *) hookdata;
- isc_result_t result = ISC_R_UNSET;
-
- UNUSED(cbdata);
-
- if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
- (qctx->filter_aaaa != dns_aaaa_filter ||
- (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
- dns_rdataset_isassociated(qctx->sigrdataset))))
- {
- *resp = result;
- return (false);
- }
-
- if (qctx->qtype == dns_rdatatype_aaaa) {
- dns_rdataset_t *trdataset;
- trdataset = ns_client_newrdataset(qctx->client);
- result = dns_db_findrdataset(qctx->db, qctx->node,
- qctx->version,
- dns_rdatatype_a, 0,
- qctx->client->now,
- trdataset, NULL);
- if (dns_rdataset_isassociated(trdataset)) {
- dns_rdataset_disassociate(trdataset);
- }
- ns_client_putrdataset(qctx->client, &trdataset);
-
- /*
- * We found an AAAA. If we also found an A, then the AAAA
- * must not be rendered.
- *
- * If the A is not in our cache, then any result other than
- * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A,
- * and so AAAAs are okay.
- *
- * We assume there is no A if we can't recurse for this
- * client. That might be the wrong answer, but what else
- * can we do? Besides, the fact that we have the AAAA and
- * are using this mechanism in the first place suggests
- * that we care more about As than AAAAs, and would have
- * cached an A if it existed.
- */
- if (result == ISC_R_SUCCESS) {
- qctx->rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
- if (qctx->sigrdataset != NULL &&
- dns_rdataset_isassociated(qctx->sigrdataset))
- {
- qctx->sigrdataset->attributes |=
- DNS_RDATASETATTR_RENDERED;
- }
- qctx->client->hookflags |= FILTER_AAAA_FILTERED;
- } else if (!qctx->authoritative &&
- RECURSIONOK(qctx->client) &&
- (result == DNS_R_DELEGATION ||
- result == ISC_R_NOTFOUND))
- {
- /*
- * This is an ugly kludge to recurse
- * for the A and discard the result.
- *
- * Continue to add the AAAA now.
- * We'll make a note to not render it
- * if the recursion for the A succeeds.
- */
- INSIST(!REDIRECT(qctx->client));
- result = ns_query_recurse(qctx->client,
- dns_rdatatype_a,
- qctx->client->query.qname,
- NULL, NULL, qctx->resuming);
- if (result == ISC_R_SUCCESS) {
- qctx->client->hookflags |=
- FILTER_AAAA_RECURSING;
- qctx->client->query.attributes |=
- NS_QUERYATTR_RECURSING;
- }
- }
- } else if (qctx->qtype == dns_rdatatype_a &&
- ((qctx->client->hookflags & FILTER_AAAA_RECURSING) != 0))
- {
-
- dns_rdataset_t *mrdataset = NULL;
- dns_rdataset_t *sigrdataset = NULL;
-
- result = dns_message_findname(qctx->client->message,
- DNS_SECTION_ANSWER, qctx->fname,
- dns_rdatatype_aaaa, 0,
- NULL, &mrdataset);
- if (result == ISC_R_SUCCESS) {
- mrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
- }
-
- result = dns_message_findname(qctx->client->message,
- DNS_SECTION_ANSWER, qctx->fname,
- dns_rdatatype_rrsig,
- dns_rdatatype_aaaa,
- NULL, &sigrdataset);
- if (result == ISC_R_SUCCESS) {
- sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
- }
-
- qctx->client->hookflags &= ~FILTER_AAAA_RECURSING;
-
- result = ns_query_done(qctx);
-
- *resp = result;
- return (true);
-
- }
-
- *resp = result;
- return (false);
-}
-
-static bool
-filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) {
- query_ctx_t *qctx = (query_ctx_t *) hookdata;
- dns_name_t *name = NULL;
- dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
- dns_rdataset_t *a = NULL;
- bool have_a = true;
-
- UNUSED(cbdata);
-
- if (qctx->filter_aaaa == dns_aaaa_ok) {
- *resp = ISC_R_UNSET;
- return (false);
- }
-
- dns_message_findname(qctx->client->message, DNS_SECTION_ANSWER,
- (qctx->fname != NULL)
- ? qctx->fname
- : qctx->tname,
- dns_rdatatype_any, 0, &name, NULL);
-
- /*
- * If we're not authoritative, just assume there's an
- * A even if it wasn't in the cache and therefore isn't
- * in the message. But if we're authoritative, then
- * if there was an A, it should be here.
- */
- if (qctx->authoritative && name != NULL) {
- dns_message_findtype(name, dns_rdatatype_a, 0, &a);
- if (a == NULL) {
- have_a = false;
- }
- }
-
- if (name != NULL) {
- dns_message_findtype(name, dns_rdatatype_aaaa, 0, &aaaa);
- dns_message_findtype(name, dns_rdatatype_rrsig,
- dns_rdatatype_aaaa, &aaaa_sig);
- }
-
- if (have_a && aaaa != NULL &&
- (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
- qctx->filter_aaaa == dns_aaaa_break_dnssec))
- {
- aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
- if (aaaa_sig != NULL) {
- aaaa_sig->attributes |= DNS_RDATASETATTR_RENDERED;
- }
- }
-
- *resp = ISC_R_UNSET;
- return (false);
-}
-
-/*
- * Hide AAAA rrsets in the additional section if there is a matching A,
- * and hide NS in the additional section if AAAA was filtered in the answer
- * section.
- */
-static bool
-filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) {
- query_ctx_t *qctx = (query_ctx_t *) hookdata;
- isc_result_t result;
-
- UNUSED(cbdata);
-
- if (qctx->filter_aaaa == dns_aaaa_ok) {
- *resp = ISC_R_UNSET;
- return (false);
- }
-
- result = dns_message_firstname(qctx->client->message,
- DNS_SECTION_ADDITIONAL);
- while (result == ISC_R_SUCCESS) {
- dns_name_t *name = NULL;
- dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
- dns_rdataset_t *a = NULL;
-
- dns_message_currentname(qctx->client->message,
- DNS_SECTION_ADDITIONAL,
- &name);
-
- result = dns_message_nextname(qctx->client->message,
- DNS_SECTION_ADDITIONAL);
-
- dns_message_findtype(name, dns_rdatatype_a, 0, &a);
- if (a == NULL) {
- continue;
- }
-
- dns_message_findtype(name, dns_rdatatype_aaaa, 0,
- &aaaa);
- if (aaaa == NULL) {
- continue;
- }
-
- dns_message_findtype(name, dns_rdatatype_rrsig,
- dns_rdatatype_aaaa, &aaaa_sig);
-
- if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
- qctx->filter_aaaa == dns_aaaa_break_dnssec)
- {
- aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
- if (aaaa_sig != NULL) {
- aaaa_sig->attributes |=
- DNS_RDATASETATTR_RENDERED;
- }
- }
- }
-
- if ((qctx->client->hookflags & FILTER_AAAA_FILTERED) != 0) {
- result = dns_message_firstname(qctx->client->message,
- DNS_SECTION_AUTHORITY);
- while (result == ISC_R_SUCCESS) {
- dns_name_t *name = NULL;
- dns_rdataset_t *ns = NULL, *ns_sig = NULL;
-
- dns_message_currentname(qctx->client->message,
- DNS_SECTION_AUTHORITY,
- &name);
-
- result = dns_message_findtype(name, dns_rdatatype_ns,
- 0, &ns);
- if (result == ISC_R_SUCCESS) {
- ns->attributes |= DNS_RDATASETATTR_RENDERED;
- }
-
- result = dns_message_findtype(name, dns_rdatatype_rrsig,
- dns_rdatatype_ns,
- &ns_sig);
- if (result == ISC_R_SUCCESS) {
- ns_sig->attributes |= DNS_RDATASETATTR_RENDERED;
- }
-
- result = dns_message_nextname(qctx->client->message,
- DNS_SECTION_AUTHORITY);
- }
- }
-
- *resp = ISC_R_UNSET;
- return (false);
-}
-
-void
-ns__query_inithooks() {
- /*
- * XXX: This function is temporary. Later, the hook table
- * will be set up when initializing hook modules after
- * configuring the server.
- *
- * For now, however, we just call this once when initializing named
- * and it will set up all the filter-aaaa hooks.
- */
-
- ns_hooktable_init(NULL);
- ns_hook_add(NULL, NS_QUERY_RESPOND_BEGIN, &filter_respbegin);
- ns_hook_add(NULL, NS_QUERY_RESPOND_ANY_FOUND, &filter_respanyfound);
- ns_hook_add(NULL, NS_QUERY_PREP_RESPONSE_BEGIN, &filter_prepresp);
- ns_hook_add(NULL, NS_QUERY_DONE_SEND, &filter_donesend);
-}
ISC_LIST_INIT(sctx->altsecrets);
- /*
- * XXX: temporary.
- */
- ns__query_inithooks();
-
sctx->magic = SCTX_MAGIC;
*sctxp = sctx;
ns_client_detach
ns_client_dumprecursing
ns_client_error
-ns_client_getsockaddr
+ns_client_findversion
ns_client_getdestaddr
+ns_client_getnamebuf
+ns_client_getsockaddr
+ns_client_keepname
ns_client_killoldestquery
ns_client_log
ns_client_logv
+ns_client_newdbversion
+ns_client_newname
+ns_client_newnamebuf
+ns_client_newrdataset
ns_client_next
+ns_client_putrdataset
ns_client_qnamereplace
ns_client_recursing
+ns_client_releasename
ns_client_replace
ns_client_send
ns_client_sendraw
ns_clientmgr_create
ns_clientmgr_createclients
ns_clientmgr_destroy
+ns_hook_add
+ns_hook_createctx
+ns_hook_destroyctx
+ns_hookmodule_cleanup
+ns_hookmodule_load
+ns_hooktable_create
+ns_hooktable_free
+ns_hooktable_init
+ns_hooktable_reset
+ns_hooktable_save
ns_interface_attach
ns_interface_detach
ns_interface_shutdown