#include <isccfg/grammar.h>
#include <isccfg/namedconf.h>
+#include <dns/acl.h>
#include <dns/result.h>
-#include <dns/view.h>
#include <ns/client.h>
#include <ns/hooks.h>
goto cleanup; \
} while (0)
-ns_hook_destroy_t hook_destroy;
-ns_hook_register_t hook_register;
-ns_hook_version_t hook_version;
+/*
+ * Set up in the register function.
+ */
+static int module_id;
+
+/*
+ * Hook data pool.
+ */
+static isc_mempool_t *datapool = NULL;
/*
* 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 & \
+/*
+ * Client attribute tests.
+ */
+#define WANTDNSSEC(c) (((c)->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
+#define RECURSIONOK(c) (((c)->query.attributes & \
NS_QUERYATTR_RECURSIONOK) != 0)
+/*
+ * Hook registration structures: pointers to these structures will
+ * be added to a hook table when this module is registered.
+ */
+static bool
+filter_qctx_initialize(void *hookdata, void *cbdata, isc_result_t *resp);
+static ns_hook_t filter_init = {
+ .callback = filter_qctx_initialize,
+};
+
static bool
filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+static ns_hook_t filter_respbegin = {
+ .callback = filter_respond_begin,
+};
static bool
filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp);
+static ns_hook_t filter_respanyfound = {
+ .callback = filter_respond_any_found,
+};
static bool
filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+static ns_hook_t filter_prepresp = {
+ .callback = filter_prep_response_begin,
+};
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 = {
+static ns_hook_t filter_donesend = {
.callback = filter_query_done_send,
};
+static bool
+filter_qctx_destroy(void *hookdata, void *cbdata, isc_result_t *resp);
+ns_hook_t filter_destroy = {
+ .callback = filter_qctx_destroy,
+};
+
+/**
+ ** Support for parsing of parameters and configuration of the module.
+ **/
+
/*
- * Configuration support.
+ * Possible values for the settings of filter-aaaa-on-v4 and
+ * filter-aaaa-on-v6: "no" is NONE, "yes" is FILTER, "break-dnssec"
+ * is BREAK_DNSSEC.
*/
+typedef enum {
+ NONE = 0,
+ FILTER = 1,
+ BREAK_DNSSEC = 2
+} filter_aaaa_t;
-static dns_aaaa_t v4_aaaa;
-static dns_aaaa_t v6_aaaa;
+/*
+ * Values configured when the module is loaded.
+ */
+static filter_aaaa_t v4_aaaa = NONE;
+static filter_aaaa_t v6_aaaa = NONE;
static dns_acl_t *aaaa_acl = NULL;
+/*
+ * Support for parsing of parameters.
+ */
static const char *filter_aaaa_enums[] = { "break-dnssec", NULL };
+
static isc_result_t
parse_filter_aaaa(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
return (cfg_parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
}
+
static void
doc_filter_aaaa(cfg_printer_t *pctx, const cfg_type_t *type) {
cfg_doc_enum_or_other(pctx, type, &cfg_type_boolean);
}
+
static cfg_type_t cfg_type_filter_aaaa = {
"filter_aaaa", parse_filter_aaaa, cfg_print_ustring,
doc_filter_aaaa, &cfg_rep_string, filter_aaaa_enums,
static isc_result_t
parse_filter_aaaa_on(const cfg_obj_t *param_obj, const char *param_name,
- dns_aaaa_t *dstp)
+ filter_aaaa_t *dstp)
{
const cfg_obj_t *obj = NULL;
isc_result_t result;
if (cfg_obj_isboolean(obj)) {
if (cfg_obj_asboolean(obj)) {
- *dstp = dns_aaaa_filter;
+ *dstp = FILTER;
} else {
- *dstp = dns_aaaa_ok;
+ *dstp = NONE;
}
} else if (strcasecmp(cfg_obj_asstring(obj), "break-dnssec") == 0) {
- *dstp = dns_aaaa_break_dnssec;
+ *dstp = BREAK_DNSSEC;
} else {
result = ISC_R_UNEXPECTED;
}
return (result);
}
+/**
+ ** Mandatory hook API functions.
+ **/
+
+/*
+ * Prototypes for the hook module API functions defined below.
+ */
+ns_hook_destroy_t hook_destroy;
+ns_hook_register_t hook_register;
+ns_hook_version_t hook_version;
+
/*
- * Mandatory hook API functions.
+ * Called by ns_hookmodule_load() to register hook functions into
+ * a hook table.
*/
isc_result_t
-hook_register(const char *parameters, const char *file, unsigned long line,
- const void *cfg, void *actx, ns_hookctx_t *hctx,
- ns_hooktable_t *hooktable, void **instp)
+hook_register(const unsigned int modid, const char *parameters,
+ const char *file, unsigned long line,
+ const void *cfg, void *actx,
+ ns_hookctx_t *hctx, ns_hooktable_t *hooktable, void **instp)
{
+ isc_result_t result;
+
UNUSED(instp);
+ module_id = modid;
+
if (parameters != NULL) {
isc_log_write(hctx->lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
"module from %s:%lu",
file, line);
- parse_parameters(parameters, cfg, actx, hctx);
+ CHECK(parse_parameters(parameters, cfg, actx, hctx));
} else {
isc_log_write(hctx->lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
file, line);
}
- ns_hook_add(hooktable, NS_QUERY_RESPOND_BEGIN,
- &filter_respbegin);
+ ns_hook_add(hooktable, NS_QUERY_QCTX_INITIALIZED, &filter_init);
+ 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);
+ ns_hook_add(hooktable, NS_QUERY_PREP_RESPONSE_BEGIN, &filter_prepresp);
+ ns_hook_add(hooktable, NS_QUERY_DONE_SEND, &filter_donesend);
+ ns_hook_add(hooktable, NS_QUERY_QCTX_DESTROYED, &filter_destroy);
+
+ CHECK(isc_mempool_create(hctx->mctx, sizeof(filter_aaaa_t),
+ &datapool));
/*
- * 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
+ * Fill the mempool with 1K filter_aaaa state objects at
+ * a time; ideally after a single allocation, the mempool will
+ * have enough to handle all the simultaneous queries the system
+ * requires and it won't be necessary to allocate more.
+ *
+ * We don't set any limit on the number of free state objects
+ * so that they'll always be returned to the pool and not
+ * freed until the pool is destroyed on shutdown.
*/
- return (ISC_R_SUCCESS);
+ isc_mempool_setfillcount(datapool, 1024);
+ isc_mempool_setfreemax(datapool, UINT_MAX);
+
+ cleanup:
+ if (result != ISC_R_SUCCESS) {
+ if (datapool != NULL) {
+ isc_mempool_destroy(&datapool);
+ }
+ }
+ return (result);
}
+/*
+ * Called by ns_hookmodule_cleanup(); frees memory allocated by
+ * the module when it was registered.
+ */
void
hook_destroy(void **instp) {
UNUSED(instp);
+ if (datapool != NULL) {
+ isc_mempool_destroy(&datapool);
+ }
if (aaaa_acl != NULL) {
dns_acl_detach(&aaaa_acl);
}
return;
}
+/*
+ * Returns hook module API version for compatibility checks.
+ */
int
hook_version(unsigned int *flags) {
UNUSED(flags);
return (NS_HOOK_VERSION);
}
+/**
+ ** "filter-aaaa" feature implementation begins here
+ **/
+
/*
* Check whether this is a V4 client.
*/
}
/*
- * 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.
+ * Shorthand to refer to the persistent data stored by this module in
+ * the query context structure.
+ */
+#define FILTER_MODE(qctx) ((filter_aaaa_t **) &qctx->hookdata[module_id])
+
+/*
+ * Initialize hook data in the query context, fetching from a memory
+ * pool.
+ */
+static bool
+filter_qctx_initialize(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
+
+ UNUSED(cbdata);
+
+ *mode = isc_mempool_get(datapool);
+ **mode = NONE;
+
+ *resp = ISC_R_UNSET;
+ return (false);
+}
+
+/*
+ * Determine whether this client should have AAAA filtered nor not,
+ * based on the client address family and the settings of
+ * filter-aaaa-on-v4 and filter-aaaa-on-v6.
*/
static bool
filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
isc_result_t result;
UNUSED(cbdata);
- qctx->filter_aaaa = dns_aaaa_ok;
- if (v4_aaaa != dns_aaaa_ok || v6_aaaa != dns_aaaa_ok) {
+ if (v4_aaaa != NONE || v6_aaaa != NONE) {
result = ns_client_checkaclsilent(qctx->client, NULL,
aaaa_acl, true);
if (result == ISC_R_SUCCESS &&
- v4_aaaa != dns_aaaa_ok &&
+ v4_aaaa != NONE &&
is_v4_client(qctx->client))
{
- qctx->filter_aaaa = v4_aaaa;
+ **mode = v4_aaaa;
} else if (result == ISC_R_SUCCESS &&
- v6_aaaa != dns_aaaa_ok &&
+ v6_aaaa != NONE &&
is_v6_client(qctx->client))
{
- qctx->filter_aaaa = v6_aaaa;
+ **mode = v6_aaaa;
}
}
}
/*
- * Optionally hide AAAA rrsets if there is a matching A.
+ * Hide AAAA rrsets if there is a matching A. Trigger recursion if
+ * necessary to find out whether an A exists.
+ *
* (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;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
isc_result_t result = ISC_R_UNSET;
UNUSED(cbdata);
- if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
- (qctx->filter_aaaa != dns_aaaa_filter ||
+ if (**mode != BREAK_DNSSEC &&
+ (**mode != FILTER ||
(WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
dns_rdataset_isassociated(qctx->sigrdataset))))
{
qctx->sigrdataset->attributes |=
DNS_RDATASETATTR_RENDERED;
}
- qctx->client->hookflags |= FILTER_AAAA_FILTERED;
+ qctx->client->hookflags[module_id] |=
+ FILTER_AAAA_FILTERED;
} else if (!qctx->authoritative &&
RECURSIONOK(qctx->client) &&
(result == DNS_R_DELEGATION ||
qctx->client->query.qname,
NULL, NULL, qctx->resuming);
if (result == ISC_R_SUCCESS) {
- qctx->client->hookflags |=
+ qctx->client->hookflags[module_id] |=
FILTER_AAAA_RECURSING;
qctx->client->query.attributes |=
NS_QUERYATTR_RECURSING;
}
}
} else if (qctx->qtype == dns_rdatatype_a &&
- ((qctx->client->hookflags & FILTER_AAAA_RECURSING) != 0))
+ ((qctx->client->hookflags[module_id] &
+ FILTER_AAAA_RECURSING) != 0))
{
dns_rdataset_t *mrdataset = NULL;
sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
}
- qctx->client->hookflags &= ~FILTER_AAAA_RECURSING;
+ qctx->client->hookflags[module_id] &= ~FILTER_AAAA_RECURSING;
result = ns_query_done(qctx);
*resp = result;
- return (true);
+ return (true);
}
*resp = result;
return (false);
}
+/*
+ * When answering an ANY query, remove AAAA if A is present.
+ */
static bool
filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
dns_name_t *name = NULL;
dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
dns_rdataset_t *a = NULL;
UNUSED(cbdata);
- if (qctx->filter_aaaa == dns_aaaa_ok) {
+ if (**mode == NONE) {
*resp = ISC_R_UNSET;
return (false);
}
if (have_a && aaaa != NULL &&
(aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
- qctx->filter_aaaa == dns_aaaa_break_dnssec))
+ **mode == BREAK_DNSSEC))
{
aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
if (aaaa_sig != NULL) {
static bool
filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) {
query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
isc_result_t result;
UNUSED(cbdata);
- if (qctx->filter_aaaa == dns_aaaa_ok) {
+ if (**mode == NONE) {
*resp = ISC_R_UNSET;
return (false);
}
dns_rdatatype_aaaa, &aaaa_sig);
if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
- qctx->filter_aaaa == dns_aaaa_break_dnssec)
+ **mode == BREAK_DNSSEC)
{
aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
if (aaaa_sig != NULL) {
}
}
- if ((qctx->client->hookflags & FILTER_AAAA_FILTERED) != 0) {
+ if ((qctx->client->hookflags[module_id] & FILTER_AAAA_FILTERED) != 0) {
result = dns_message_firstname(qctx->client->message,
DNS_SECTION_AUTHORITY);
while (result == ISC_R_SUCCESS) {
*resp = ISC_R_UNSET;
return (false);
}
+
+/*
+ * Return hook data to the mempool.
+ */
+static bool
+filter_qctx_destroy(void *hookdata, void *cbdata, isc_result_t *resp) {
+ query_ctx_t *qctx = (query_ctx_t *) hookdata;
+ filter_aaaa_t **mode = FILTER_MODE(qctx);
+
+ UNUSED(cbdata);
+
+ if (*mode != NULL) {
+ isc_mempool_put(datapool, *mode);
+ *mode = NULL;
+ }
+
+ *resp = ISC_R_UNSET;
+ return (false);
+}
}
static isc_result_t
-configure_hook(ns_hooktable_t *hooktable, const cfg_obj_t *hook,
+configure_hook(ns_hooktable_t *hooktable, const unsigned int modid,
+ const cfg_obj_t *hook,
const cfg_obj_t *config, ns_hookctx_t *hctx)
{
isc_result_t result = ISC_R_SUCCESS;
obj = cfg_tuple_get(hook, "parameters");
if (obj != NULL && cfg_obj_isstring(obj)) {
- result = ns_hookmodule_load(library,
+ result = ns_hookmodule_load(library, modid,
cfg_obj_asstring(obj),
cfg_obj_file(obj),
cfg_obj_line(obj),
named_g_aclconfctx,
hctx, hooktable);
} else {
- result = ns_hookmodule_load(library, NULL,
+ result = ns_hookmodule_load(library, modid, NULL,
cfg_obj_file(hook),
cfg_obj_line(hook),
config,
unsigned int resolver_param;
dns_ntatable_t *ntatable = NULL;
const char *qminmode = NULL;
+ unsigned int module_counter = 0;
REQUIRE(DNS_VIEW_VALID(view));
CHECK(ns_hook_createctx(mctx, &hctx));
}
- CHECK(configure_hook(view->hooktable, hook, config, hctx));
+ CHECK(configure_hook(view->hooktable, module_counter,
+ hook, config, hctx));
+
+ module_counter++;
}
#endif
dyndb <string> <quoted_string> {
<unspecified-text> }; // may occur multiple times
+hook ( query ) <string> [ { <unspecified-text> }
+ ]; // may occur multiple times
+
key <string> {
algorithm <string>;
secret <string>;
fetches-per-server <integer> [ ( drop | fail ) ];
fetches-per-zone <integer> [ ( drop | fail ) ];
files ( default | unlimited | <sizeval> );
- filter-aaaa { <address_match_element>; ... };
- filter-aaaa-on-v4 ( break-dnssec | <boolean> );
- filter-aaaa-on-v6 ( break-dnssec | <boolean> );
+ filter-aaaa { <address_match_element>; ... }; // obsolete
+ filter-aaaa-on-v4 <boolean>; // obsolete
+ filter-aaaa-on-v6 <boolean>; // obsolete
flush-zones-on-shutdown <boolean>;
forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
fetch-quota-params <integer> <fixedpoint> <fixedpoint> <fixedpoint>;
fetches-per-server <integer> [ ( drop | fail ) ];
fetches-per-zone <integer> [ ( drop | fail ) ];
- filter-aaaa { <address_match_element>; ... };
- filter-aaaa-on-v4 ( break-dnssec | <boolean> );
- filter-aaaa-on-v6 ( break-dnssec | <boolean> );
+ filter-aaaa { <address_match_element>; ... }; // obsolete
+ filter-aaaa-on-v4 <boolean>; // obsolete
+ filter-aaaa-on-v6 <boolean>; // obsolete
forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
| <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... };
glue-cache <boolean>;
+ hook ( query ) <string> [ {
+ <unspecified-text> } ]; // may occur multiple times
inline-signing <boolean>;
ixfr-from-differences ( primary | master | secondary | slave |
<boolean> );
dns_masterformat_map = 3
} dns_masterformat_t;
-typedef enum {
- dns_aaaa_ok = 0,
- dns_aaaa_filter = 1,
- dns_aaaa_break_dnssec = 2
-} dns_aaaa_t;
-
/*
* These are generated by gen.c.
*/
ISC_QLINK_INIT(client, ilink);
client->keytag = NULL;
client->keytag_len = 0;
- client->hookflags = 0;
+ memset(client->hookflags, 0, sizeof(client->hookflags));
/*
* We call the init routines for the various kinds of client here,
#endif /* HAVE_DLFCN_H */
isc_result_t
-ns_hookmodule_load(const char *libname, const char *parameters,
+ns_hookmodule_load(const char *libname, const unsigned int modid,
+ const char *parameters,
const char *file, unsigned long line,
const void *cfg, void *actx,
ns_hookctx_t *hctx, ns_hooktable_t *hooktable)
"loading module '%s'", libname);
CHECK(load_library(hctx->mctx, libname, &module));
- CHECK(module->register_func(parameters, file, line,
+ CHECK(module->register_func(modid, parameters, file, line,
cfg, actx, hctx, hooktable,
&module->inst));
* Allows a hook module to set flags
* that persist across recursion.
*/
- uint32_t hookflags;
+ uint32_t hookflags[NS_MAX_MODULES];
};
typedef ISC_QUEUE(ns_client_t) client_queue_t;
*/
typedef enum {
- NS_QUERY_SETUP_QCTX_INITIALIZED,
+ NS_QUERY_QCTX_INITIALIZED,
+ NS_QUERY_QCTX_DESTROYED,
NS_QUERY_START_BEGIN,
NS_QUERY_LOOKUP_BEGIN,
NS_QUERY_RESUME_BEGIN,
#define NS_HOOK_AGE 0
#endif
-typedef isc_result_t ns_hook_register_t(const char *parameters,
+typedef isc_result_t ns_hook_register_t(const unsigned int modid,
+ const char *parameters,
const char *file,
unsigned long line,
const void *cfg,
ns_hook_destroyctx(ns_hookctx_t **hctxp);
isc_result_t
-ns_hookmodule_load(const char *libname, const char *parameters,
+ns_hookmodule_load(const char *libname, const unsigned int modid,
+ const char *parameters,
const char *file, unsigned long line,
const void *cfg, void *actx,
ns_hookctx_t *hctx, ns_hooktable_t *hooktable);
#include <ns/types.h>
+/*
+ * Maximum number of query hook modules that can be configured;
+ * more than this will overflow qctx->hookdata.
+ */
+#define NS_MAX_MODULES 16
+
/*% nameserver database version structure */
typedef struct ns_dbversion {
dns_db_t *db;
dns_dbversion_t *version;
- bool acl_checked;
- bool queryok;
+ bool acl_checked;
+ bool queryok;
ISC_LINK(struct ns_dbversion) link;
} ns_dbversion_t;
struct ns_query {
unsigned int attributes;
unsigned int restarts;
- bool timerset;
+ bool timerset;
dns_name_t * qname;
dns_name_t * origqname;
dns_rdatatype_t qtype;
dns_db_t * gluedb;
dns_db_t * authdb;
dns_zone_t * authzone;
- bool authdbset;
- bool isreferral;
+ bool authdbset;
+ bool isreferral;
isc_mutex_t fetchlock;
dns_fetch_t * fetch;
dns_fetch_t * prefetch;
ISC_LIST(ns_dbversion_t) freeversions;
dns_rdataset_t * dns64_aaaa;
dns_rdataset_t * dns64_sigaaaa;
- bool * dns64_aaaaok;
+ bool * dns64_aaaaok;
unsigned int dns64_aaaaoklen;
unsigned int dns64_options;
unsigned int dns64_ttl;
isc_result_t result;
dns_rdataset_t * rdataset;
dns_rdataset_t * sigrdataset;
- bool authoritative;
- bool is_zone;
+ bool authoritative;
+ bool is_zone;
} redirect;
ns_query_recparam_t recparam;
dns_rpz_st_t *rpz_st; /* RPZ state */
dns_zone_t *zone; /* zone to search */
- dns_aaaa_t filter_aaaa; /* AAAA filtering */
+ dns_view_t *view; /* client view */
+
+ void *hookdata[NS_MAX_MODULES]; /* data used by query hooks */
isc_result_t result; /* query result */
int line; /* line to report error */
* (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.
- */
-
#endif /* NS_QUERY_H */
ns_hooktable_t *_tab = ns__hook_table; \
query_ctx_t *_q = (_qctx); \
if (_q != NULL && \
- _q->client != NULL && \
- _q->client->view != NULL && \
- _q->client->view->hooktable != NULL) \
+ _q->view != NULL && \
+ _q->view->hooktable != NULL) \
{ \
- _tab = _q->client->view->hooktable; \
+ _tab = _q->view->hooktable; \
} \
NS_PROCESS_HOOK(_tab, _id, _q, __VA_ARGS__); \
} while (false)
ns_hooktable_t *_tab = ns__hook_table; \
query_ctx_t *_q = (_qctx); \
if (_q != NULL && \
- _q->client != NULL && \
- _q->client->view != NULL && \
- _q->client->view->hooktable != NULL) \
+ _q->view != NULL && \
+ _q->view->hooktable != NULL) \
{ \
- _tab = _q->client->view->hooktable; \
+ _tab = _q->view->hooktable; \
} \
NS_PROCESS_HOOK_VOID(_tab, _id, _q, __VA_ARGS__); \
} while (false)
* return it to the client.
*
* (XXX: This description omits several special cases including
- * DNS64, filter-aaaa, RPZ, RRL, and the SERVFAIL cache.)
+ * DNS64, RPZ, RRL, and the SERVFAIL cache. It also doesn't discuss
+ * query hook modules.)
*/
static void
* If we want only minimal responses and are here, then it must
* be for glue.
*/
- if (client->view->minimalresponses == dns_minimal_yes) {
+ if (qctx->view->minimalresponses == dns_minimal_yes) {
goto try_glue;
}
*/
try_cache:
- if (!client->view->recursion) {
+ if (!qctx->view->recursion) {
goto try_glue;
}
client->now, &node, fname, &cm, &ci,
rdataset, sigrdataset);
- dns_cache_updatestats(client->view->cache, result);
+ dns_cache_updatestats(qctx->view->cache, result);
if (!WANTDNSSEC(client)) {
ns_client_putrdataset(client, &sigrdataset);
}
CTRACE(ISC_LOG_DEBUG(3), "query_setorder");
- if (client->view->order != NULL) {
+ UNUSED(client);
+
+ if (qctx->view->order != NULL) {
rdataset->attributes |=
- dns_order_find(qctx->client->view->order,
+ dns_order_find(qctx->view->order,
name, rdataset->type,
rdataset->rdclass);
}
/*
* Try to process glue directly.
*/
- if (client->view->use_glue_cache &&
+ if (qctx->view->use_glue_cache &&
(rdataset->type == dns_rdatatype_ns) &&
(client->query.gluedb != NULL) &&
dns_db_iszone(client->query.gluedb))
* Initialize query context 'qctx'. Run by query_setup() when
* first handling a client query, and by query_resume() when
* returning from recursion.
+ *
+ * Whenever this function is called, qctx_destroy() must be called
+ * when leaving the scope or freeing the qctx.
*/
static void
qctx_init(ns_client_t *client, dns_fetchevent_t *event,
REQUIRE(qctx != NULL);
REQUIRE(client != NULL);
+ memset(qctx, 0, sizeof(query_ctx_t));
+
/* Set this first so CCTRACE will work */
qctx->client = client;
+ dns_view_attach(client->view, &qctx->view);
- CCTRACE(ISC_LOG_DEBUG(3), "qctx_create");
+ CCTRACE(ISC_LOG_DEBUG(3), "qctx_init");
qctx->event = event;
qctx->qtype = qctx->type = qtype;
qctx->options = 0;
qctx->resuming = false;
qctx->is_zone = false;
- qctx->findcoveringnsec = client->view->synthfromdnssec;
+ qctx->findcoveringnsec = qctx->view->synthfromdnssec;
qctx->is_staticstub_zone = false;
qctx->nxrewrite = false;
qctx->answer_has_ns = false;
qctx->authoritative = false;
- qctx->filter_aaaa = dns_aaaa_ok;
+
+ PROCESS_HOOK_VOID(NS_QUERY_QCTX_INITIALIZED, qctx);
}
/*%
}
}
+static void
+qctx_destroy(query_ctx_t *qctx) {
+ PROCESS_HOOK_VOID(NS_QUERY_QCTX_DESTROYED, qctx);
+ dns_view_detach(&qctx->view);
+}
+
/*%
* Log detailed information about the query immediately after
* the client request or a return from recursion.
qctx.type = dns_rdatatype_any;
}
- PROCESS_HOOK(NS_QUERY_SETUP_QCTX_INITIALIZED, &qctx);
-
/*
* Check SERVFAIL cache
*/
result = ns__query_sfcache(&qctx);
if (result != ISC_R_COMPLETE) {
+ qctx_destroy(&qctx);
return (result);
}
- return (ns__query_start(&qctx));
+ result = ns__query_start(&qctx);
+ qctx_destroy(&qctx);
+ return (result);
}
static bool
* If we require a server cookie then send back BADCOOKIE
* before we have done too much work.
*/
- if (!TCP(qctx->client) && qctx->client->view->requireservercookie &&
+ if (!TCP(qctx->client) && qctx->view->requireservercookie &&
WANTCOOKIE(qctx->client) && !HAVECOOKIE(qctx->client))
{
qctx->client->message->flags &= ~DNS_MESSAGEFLAG_AA;
return (ns_query_done(qctx));
}
- if (qctx->client->view->checknames &&
+ if (qctx->view->checknames &&
!dns_rdata_checkowner(qctx->client->query.qname,
qctx->client->message->rdclass,
qctx->qtype, false))
/*
* Setup for root key sentinel processing.
*/
- if (qctx->client->view->root_key_sentinel &&
+ if (qctx->view->root_key_sentinel &&
qctx->client->query.restarts == 0 &&
(qctx->qtype == dns_rdatatype_a ||
qctx->qtype == dns_rdatatype_aaaa) &&
}
if (!qctx->is_zone) {
- dns_cache_updatestats(qctx->client->view->cache, result);
+ dns_cache_updatestats(qctx->view->cache, result);
}
if ((qctx->client->query.dboptions & DNS_DBFIND_STALEOK) != 0) {
if (dns_rdataset_isassociated(qctx->rdataset) &&
dns_rdataset_count(qctx->rdataset) > 0 &&
STALE(qctx->rdataset)) {
- qctx->rdataset->ttl =
- qctx->client->view->staleanswerttl;
+ qctx->rdataset->ttl = qctx->view->staleanswerttl;
success = true;
} else {
success = false;
} else {
query_ctx_t qctx;
+ /*
+ * Initalize a new qctx and use it to resume
+ * from recursion.
+ */
qctx_init(client, devent, 0, &qctx);
query_trace(&qctx);
errorloglevel, false);
}
}
+
+ qctx_destroy(&qctx);
}
dns_resolver_destroyfetch(&fetch);
/*
* Has response policy changed out from under us?
*/
- if (qctx->rpz_st->rpz_ver != qctx->client->view->rpzs->rpz_ver)
+ if (qctx->rpz_st->rpz_ver != qctx->view->rpzs->rpz_ver)
{
ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_QUERY,
"query_resume: RPZ settings "
"out of date "
"(rpz_ver %d, expected %d)",
- qctx->client->view->rpzs->rpz_ver,
+ qctx->view->rpzs->rpz_ver,
qctx->rpz_st->rpz_ver);
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
return (ns_query_done(qctx));
failcache = false;
} else {
failcache =
- dns_badcache_find(qctx->client->view->failcache,
+ dns_badcache_find(qctx->view->failcache,
qctx->client->query.qname,
qctx->qtype, &flags,
&qctx->client->tnow);
}
#else
- failcache = dns_badcache_find(qctx->client->view->failcache,
+ failcache = dns_badcache_find(qctx->view->failcache,
qctx->client->query.qname,
qctx->qtype, &flags,
&qctx->client->tnow);
* is set when we are called the second time preventing the
* response being dropped.
*/
- ns_client_log(qctx->client, DNS_LOGCATEGORY_RRL, NS_LOGMODULE_QUERY,
- ISC_LOG_DEBUG(99), "rrl=%p, HAVECOOKIE=%u, result=%s, "
+ ns_client_log(qctx->client, DNS_LOGCATEGORY_RRL,
+ NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(99),
+ "rrl=%p, HAVECOOKIE=%u, result=%s, "
"fname=%p(%u), is_zone=%u, RECURSIONOK=%u, "
"query.rpz_st=%p(%u), RRL_CHECKED=%u\n",
qctx->client->view->rrl, HAVECOOKIE(qctx->client),
isc_result_toid(result), qctx->fname,
- qctx->fname?dns_name_isabsolute(qctx->fname) : 0,
+ qctx->fname != NULL
+ ? dns_name_isabsolute(qctx->fname)
+ : 0,
qctx->is_zone, RECURSIONOK(qctx->client),
qctx->client->query.rpz_st,
- qctx->client->query.rpz_st ?
- (qctx->client->query.rpz_st->state & DNS_RPZ_REWRITTEN) != 0 : 0,
- (qctx->client->query.attributes & NS_QUERYATTR_RRL_CHECKED) != 0);
-
- if (qctx->client->view->rrl != NULL &&
+ qctx->client->query.rpz_st != NULL
+ ? ((qctx->client->query.rpz_st->state &
+ DNS_RPZ_REWRITTEN) != 0)
+ : 0,
+ (qctx->client->query.attributes &
+ NS_QUERYATTR_RRL_CHECKED) != 0);
+
+ if (qctx->view->rrl != NULL &&
!HAVECOOKIE(qctx->client) &&
((qctx->fname != NULL && dns_name_isabsolute(qctx->fname)) ||
(result == ISC_R_NOTFOUND && !RECURSIONOK(qctx->client))) &&
resp_result = ISC_R_SUCCESS;
}
- rrl_result = dns_rrl(qctx->client->view,
+ rrl_result = dns_rrl(qctx->view,
&qctx->client->peeraddr,
TCP(qctx->client),
qctx->client->message->rdclass,
"%s", log_buf);
}
- if (!qctx->client->view->rrl->log_only) {
+ if (!qctx->view->rrl->log_only) {
if (rrl_result == DNS_RRL_RESULT_DROP) {
/*
* These will also be counted in
dns_keynode_t *keynode = NULL;
isc_result_t result;
- result = dns_view_getsecroots(qctx->client->view, &keytable);
+ result = dns_view_getsecroots(qctx->view, &keytable);
if (result != ISC_R_SUCCESS) {
return (false);
}
* ANY queries.
*/
dns_rdataset_disassociate(qctx->rdataset);
- } else if (qctx->client->view->minimal_any &&
+ } else if (qctx->view->minimal_any &&
!TCP(qctx->client) && !WANTDNSSEC(qctx->client) &&
qctx->qtype == dns_rdatatype_any &&
(qctx->rdataset->type == dns_rdatatype_sig ||
CCTRACE(ISC_LOG_DEBUG(5), "query_respond_any: "
"minimal-any skip signature");
dns_rdataset_disassociate(qctx->rdataset);
- } else if (qctx->client->view->minimal_any &&
+ } else if (qctx->view->minimal_any &&
!TCP(qctx->client) && onetype != 0 &&
qctx->rdataset->type != onetype &&
qctx->rdataset->covers != onetype)
INSIST(qctx->client->query.dns64_aaaaok == NULL);
if (qctx->qtype == dns_rdatatype_aaaa && !qctx->dns64_exclude &&
- !ISC_LIST_EMPTY(qctx->client->view->dns64) &&
+ !ISC_LIST_EMPTY(qctx->view->dns64) &&
qctx->client->message->rdclass == dns_rdataclass_in &&
!dns64_aaaaok(qctx->client, qctx->rdataset, qctx->sigrdataset))
{
* If the cache doesn't even have the root NS,
* try to get that from the hints DB.
*/
- if (qctx->client->view->hints != NULL) {
+ if (qctx->view->hints != NULL) {
dns_clientinfomethods_t cm;
dns_clientinfo_t ci;
dns_clientinfomethods_init(&cm, ns_client_sourceip);
dns_clientinfo_init(&ci, qctx->client, NULL);
- dns_db_attach(qctx->client->view->hints, &qctx->db);
+ dns_db_attach(qctx->view->hints, &qctx->db);
result = dns_db_findext(qctx->db, dns_rootname,
NULL, dns_rdatatype_ns,
0, qctx->client->now, &qctx->node,
SAVE(qctx->zversion, qctx->version);
SAVE(qctx->zrdataset, qctx->rdataset);
SAVE(qctx->zsigrdataset, qctx->sigrdataset);
- dns_db_attach(qctx->client->view->cachedb, &qctx->db);
+ dns_db_attach(qctx->view->cachedb, &qctx->db);
qctx->is_zone = false;
return (query_lookup(qctx));
#endif
} else if ((result == DNS_R_NXRRSET ||
result == DNS_R_NCACHENXRRSET) &&
- !ISC_LIST_EMPTY(qctx->client->view->dns64) &&
+ !ISC_LIST_EMPTY(qctx->view->dns64) &&
qctx->client->message->rdclass == dns_rdataclass_in &&
qctx->qtype == dns_rdatatype_aaaa)
{
dns_name_t *name = NULL;
isc_buffer_t *dbuf, b;
isc_result_t result;
- dns_rdataset_t *clone = NULL, *sigclone = NULL;
+ dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
dns_rdataset_t **sigrdatasetp;
/*
}
dns_name_copy(qctx->client->query.qname, name, NULL);
- clone = ns_client_newrdataset(qctx->client);
- if (clone == NULL) {
+ cloneset = ns_client_newrdataset(qctx->client);
+ if (cloneset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
- dns_rdataset_clone(rdataset, clone);
+ dns_rdataset_clone(rdataset, cloneset);
/*
* Add answer RRset. Omit the RRSIG if DNSSEC was not requested.
*/
if (WANTDNSSEC(qctx->client)) {
- sigclone = ns_client_newrdataset(qctx->client);
- if (sigclone == NULL) {
+ clonesigset = ns_client_newrdataset(qctx->client);
+ if (clonesigset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
- dns_rdataset_clone(sigrdataset, sigclone);
- sigrdatasetp = &sigclone;
+ dns_rdataset_clone(sigrdataset, clonesigset);
+ sigrdatasetp = &clonesigset;
} else {
sigrdatasetp = NULL;
}
- query_addrrset(qctx, &name, &clone, sigrdatasetp,
+ query_addrrset(qctx, &name, &cloneset, sigrdatasetp,
dbuf, DNS_SECTION_ANSWER);
if (WANTDNSSEC(qctx->client)) {
if (name != NULL) {
ns_client_releasename(qctx->client, &name);
}
- if (clone != NULL) {
- ns_client_putrdataset(qctx->client, &clone);
+ if (cloneset != NULL) {
+ ns_client_putrdataset(qctx->client, &cloneset);
}
- if (sigclone != NULL) {
- ns_client_putrdataset(qctx->client, &sigclone);
+ if (clonesigset != NULL) {
+ ns_client_putrdataset(qctx->client, &clonesigset);
}
return (result);
}
dns_ttl_t ttl;
isc_buffer_t *dbuf, b;
isc_result_t result;
- dns_rdataset_t *clone = NULL, *sigclone = NULL;
+ dns_rdataset_t *cloneset = NULL, *clonesigset = NULL;
/*
* Detemine the correct TTL to use for the SOA and RRSIG
dns_name_copy(nowild, name, NULL);
- clone = ns_client_newrdataset(qctx->client);
- sigclone = ns_client_newrdataset(qctx->client);
- if (clone == NULL || sigclone == NULL) {
+ cloneset = ns_client_newrdataset(qctx->client);
+ clonesigset = ns_client_newrdataset(qctx->client);
+ if (cloneset == NULL || clonesigset == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
- dns_rdataset_clone(nowildrdataset, clone);
- dns_rdataset_clone(signowildrdataset, sigclone);
+ dns_rdataset_clone(nowildrdataset, cloneset);
+ dns_rdataset_clone(signowildrdataset, clonesigset);
/*
* Add NOWILDCARD proof.
*/
- query_addrrset(qctx, &name, &clone, &sigclone,
+ query_addrrset(qctx, &name, &cloneset, &clonesigset,
dbuf, DNS_SECTION_AUTHORITY);
}
if (name != NULL) {
ns_client_releasename(qctx->client, &name);
}
- if (clone != NULL) {
- ns_client_putrdataset(qctx->client, &clone);
+ if (cloneset != NULL) {
+ ns_client_putrdataset(qctx->client, &cloneset);
}
- if (sigclone != NULL) {
- ns_client_putrdataset(qctx->client, &sigclone);
+ if (clonesigset != NULL) {
+ ns_client_putrdataset(qctx->client, &clonesigset);
}
return (result);
}
if (qctx->type == dns_rdatatype_any) { /* XXX not yet */
goto cleanup;
}
- if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
+ if (!ISC_LIST_EMPTY(qctx->view->dns64) &&
(qctx->type == dns_rdatatype_a ||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
{
if (qctx->type == dns_rdatatype_any) { /* XXX not yet */
goto cleanup;
}
- if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
+ if (!ISC_LIST_EMPTY(qctx->view->dns64) &&
(qctx->type == dns_rdatatype_a ||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
{
query_glueanswer(qctx);
if (qctx->client->message->rcode == dns_rcode_nxdomain &&
- qctx->client->view->auth_nxdomain == true)
+ qctx->view->auth_nxdomain == true)
{
qctx->client->message->flags |= DNS_MESSAGEFLAG_AA;
}
*/
ns_hooktable_init(&query_hooks);
- ns_hook_add(&query_hooks, NS_QUERY_SETUP_QCTX_INITIALIZED, &hook);
+ ns_hook_add(&query_hooks, NS_QUERY_QCTX_INITIALIZED, &hook);
saved_hook_table = ns__hook_table;
ns__hook_table = &query_hooks;
"isc-spnego",
"native-pkcs11",
"openssl-hash",
- "filter-aaaa",
"querytrace",
"rpz-nsdname",
"rpz-nsip");