mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 14:35:26 +00:00
[master] expanded libns unit tests
4772. [test] Expanded unit testing framework for libns, using hooks to interrupt query flow and inspect state at specified locations. [RT #46173]
This commit is contained in:
4
CHANGES
4
CHANGES
@@ -1,3 +1,7 @@
|
||||
4772. [test] Expanded unit testing framework for libns, using
|
||||
hooks to interrupt query flow and inspect state
|
||||
at specified locations. [RT #46173]
|
||||
|
||||
4771. [bug] When sending RFC 5011 refresh queries, disregard
|
||||
cached DNSKEY rrsets. [RT #46251]
|
||||
|
||||
|
17
configure
vendored
17
configure
vendored
@@ -940,6 +940,7 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@@ -1103,6 +1104,7 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
||||
@@ -1355,6 +1357,15 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@@ -1492,7 +1503,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir
|
||||
libdir localedir mandir runstatedir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@@ -1645,6 +1656,7 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@@ -11466,7 +11478,7 @@ fi
|
||||
XTARGETS=
|
||||
case "$enable_developer" in
|
||||
yes)
|
||||
STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1"
|
||||
STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1 -DNS_HOOKS_ENABLE=1"
|
||||
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
||||
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
||||
test "${with_atf+set}" = set || with_atf=yes
|
||||
@@ -22225,6 +22237,7 @@ if test "no" != "$atf"; then
|
||||
$as_echo "#define ATF_TEST 1" >>confdefs.h
|
||||
|
||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||
STD_CDEFINES="$STD_CDEFINES -DNS_HOOKS_ENABLE=1"
|
||||
ATFBIN="$atf/bin"
|
||||
ATFLIBS="-L$atf/lib -latf-c"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exp in -lm" >&5
|
||||
|
@@ -62,7 +62,7 @@ AC_ARG_ENABLE(developer, [ --enable-developer enable developer build setti
|
||||
XTARGETS=
|
||||
case "$enable_developer" in
|
||||
yes)
|
||||
STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1"
|
||||
STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1 -DNS_HOOKS_ENABLE=1"
|
||||
test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
||||
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
||||
test "${with_atf+set}" = set || with_atf=yes
|
||||
@@ -4771,6 +4771,7 @@ ATFLIBS=
|
||||
if test "no" != "$atf"; then
|
||||
AC_DEFINE(ATF_TEST, 1, [define if ATF unit tests are to be built.])
|
||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||
STD_CDEFINES="$STD_CDEFINES -DNS_HOOKS_ENABLE=1"
|
||||
ATFBIN="$atf/bin"
|
||||
ATFLIBS="-L$atf/lib -latf-c"
|
||||
AC_CHECK_LIB(m, exp, libm=yes, libm=no)
|
||||
|
142
lib/ns/hooks.h
Normal file
142
lib/ns/hooks.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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/.
|
||||
*/
|
||||
|
||||
#ifndef NS_HOOKS_H
|
||||
#define NS_HOOKS_H 1
|
||||
|
||||
#ifdef NS_HOOKS_ENABLE
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <isc/result.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:
|
||||
*
|
||||
* - hook-related types and macros are not placed in libns header files,
|
||||
* - hook-related code is compiled away unless --enable-developer is used,
|
||||
* - hook-related macro names are prefixed with "NS_".
|
||||
*
|
||||
* However, the implementation is pretty generic and could be repurposed for
|
||||
* general use, e.g. as part of libisc, after some further customization.
|
||||
*
|
||||
* Hooks are created by inserting a macro into any function returning
|
||||
* isc_result_t (NS_PROCESS_HOOK()) or void (NS_PROCESS_HOOK_VOID()). Each
|
||||
* hook has an identifier, which is an integer that is an index into the hook
|
||||
* table. In an attempt to keep things as simple as possible, current
|
||||
* implementation:
|
||||
*
|
||||
* - uses hook tables which are statically-sized arrays only allowing a
|
||||
* single callback to be invoked for each hook identifier,
|
||||
* - only supports replacing whole hook tables.
|
||||
*
|
||||
* Hook callbacks are functions which:
|
||||
*
|
||||
* - return a boolean value; if ISC_TRUE is returned by the callback, the
|
||||
* function into which the hook is inserted will return at hook insertion
|
||||
* point; if ISC_FALSE is returned by the callback, execution of the
|
||||
* function into which the hook is inserted continues normally,
|
||||
*
|
||||
* - accept three pointers as arguments:
|
||||
*
|
||||
* - a pointer specified by the hook itself,
|
||||
* - a pointer specified upon inserting the callback into the hook table,
|
||||
* - a pointer to isc_result_t which will be returned by the function
|
||||
* into which the hook is inserted if the callback returns ISC_TRUE.
|
||||
*
|
||||
* In order for a hook callback to be called for a given hook, a pointer to
|
||||
* that callback (along with an optional pointer to callback-specific data) has
|
||||
* to be inserted into the hook table entry for that hook.
|
||||
*
|
||||
* Consider the following sample code:
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
* const ns_hook_t *foo_hook_table = NULL;
|
||||
*
|
||||
* isc_result_t
|
||||
* foo_bar(void) {
|
||||
* int val = 42;
|
||||
* ...
|
||||
* NS_PROCESS_HOOK(foo_hook_table, FOO_EXTRACT_VAL, &val);
|
||||
* ...
|
||||
* printf("This message may not be printed due to use of hooks.");
|
||||
*
|
||||
* return (ISC_R_SUCCESS);
|
||||
* }
|
||||
*
|
||||
* isc_boolean_t
|
||||
* cause_failure(void *hook_data, void *callback_data, isc_result_t *resultp) {
|
||||
* ...
|
||||
* *resultp = ISC_R_FAILURE;
|
||||
*
|
||||
* return (ISC_TRUE);
|
||||
* }
|
||||
*
|
||||
* void
|
||||
* test_foo_bar(void) {
|
||||
* isc_boolean_t foo_bar_called = ISC_FALSE;
|
||||
* const ns_hook_t my_hooks[FOO_HOOKS_COUNT] = {
|
||||
* [FOO_EXTRACT_VAL] = {
|
||||
* .callback = cause_failure,
|
||||
* .callback_data = &foo_bar_called,
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* foo_hook_table = my_hooks;
|
||||
*
|
||||
* foo_bar();
|
||||
* }
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* When test_foo_bar() is called, the hook table is first replaced. Then
|
||||
* foo_bar() gets invoked. Once execution reaches the insertion point for hook
|
||||
* FOO_EXTRACT_VAL, cause_failure() will be called with &val as hook_data and
|
||||
* &foo_bar_called as callback_data. It can do whatever it pleases with these
|
||||
* two values. Eventually, cause_failure() sets *resultp to ISC_R_FAILURE and
|
||||
* returns ISC_TRUE, which causes foo_bar() to return ISC_R_FAILURE and never
|
||||
* execute the printf() call below hook insertion point.
|
||||
*/
|
||||
|
||||
enum {
|
||||
NS_QUERY_SETUP_QCTX_INITIALIZED,
|
||||
NS_QUERY_LOOKUP_BEGIN,
|
||||
NS_QUERY_DONE_BEGIN,
|
||||
NS_QUERY_HOOKS_COUNT
|
||||
};
|
||||
|
||||
typedef isc_boolean_t
|
||||
(*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;
|
||||
} ns_hook_t;
|
||||
|
||||
#define _NS_PROCESS_HOOK(table, id, data, ...) \
|
||||
if (table != NULL) { \
|
||||
ns_hook_cb_t _callback = table[id].callback; \
|
||||
void *_callback_data = table[id].callback_data; \
|
||||
isc_result_t _result; \
|
||||
\
|
||||
if (_callback != NULL && \
|
||||
_callback(data, _callback_data, &_result)) { \
|
||||
return __VA_ARGS__; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define NS_PROCESS_HOOK(table, id, data) \
|
||||
_NS_PROCESS_HOOK(table, id, data, _result)
|
||||
|
||||
#define NS_PROCESS_HOOK_VOID(table, id, data) \
|
||||
_NS_PROCESS_HOOK(table, id, data)
|
||||
|
||||
LIBNS_EXTERNAL_DATA extern const ns_hook_t *ns__hook_table;
|
||||
|
||||
#endif /* NS_HOOKS_ENABLE */
|
||||
#endif /* NS_HOOKS_H */
|
@@ -16,6 +16,7 @@
|
||||
#include <isc/netaddr.h>
|
||||
|
||||
#include <dns/rdataset.h>
|
||||
#include <dns/resolver.h>
|
||||
#include <dns/rpz.h>
|
||||
#include <dns/types.h>
|
||||
|
||||
@@ -92,6 +93,55 @@ struct ns_query {
|
||||
#define NS_QUERYATTR_RRL_CHECKED 0x10000
|
||||
#define NS_QUERYATTR_REDIRECT 0x20000
|
||||
|
||||
/* query context structure */
|
||||
|
||||
typedef struct query_ctx {
|
||||
isc_buffer_t *dbuf; /* name buffer */
|
||||
dns_name_t *fname; /* found name from DB lookup */
|
||||
dns_rdataset_t *rdataset; /* found rdataset */
|
||||
dns_rdataset_t *sigrdataset; /* found sigrdataset */
|
||||
dns_rdataset_t *noqname; /* rdataset needing
|
||||
* NOQNAME proof */
|
||||
dns_rdatatype_t qtype;
|
||||
dns_rdatatype_t type;
|
||||
|
||||
unsigned int options; /* DB lookup options */
|
||||
|
||||
isc_boolean_t redirected; /* nxdomain redirected? */
|
||||
isc_boolean_t is_zone; /* is DB a zone DB? */
|
||||
isc_boolean_t is_staticstub_zone;
|
||||
isc_boolean_t resuming; /* resumed from recursion? */
|
||||
isc_boolean_t dns64, dns64_exclude, rpz;
|
||||
isc_boolean_t authoritative; /* authoritative query? */
|
||||
isc_boolean_t want_restart; /* CNAME chain or other
|
||||
* restart needed */
|
||||
isc_boolean_t need_wildcardproof; /* wilcard proof needed */
|
||||
isc_boolean_t nxrewrite; /* negative answer from RPZ */
|
||||
isc_boolean_t findcoveringnsec; /* lookup covering NSEC */
|
||||
isc_boolean_t want_stale; /* want stale records? */
|
||||
dns_fixedname_t wildcardname; /* name needing wcard proof */
|
||||
dns_fixedname_t dsname; /* name needing DS */
|
||||
|
||||
ns_client_t *client; /* client object */
|
||||
dns_fetchevent_t *event; /* recursion event */
|
||||
|
||||
dns_db_t *db; /* zone or cache database */
|
||||
dns_dbversion_t *version; /* DB version */
|
||||
dns_dbnode_t *node; /* DB node */
|
||||
|
||||
dns_db_t *zdb; /* zone DB values, saved */
|
||||
dns_name_t *zfname; /* while searching cache */
|
||||
dns_dbversion_t *zversion; /* for a better answer */
|
||||
dns_rdataset_t *zrdataset;
|
||||
dns_rdataset_t *zsigrdataset;
|
||||
|
||||
dns_rpz_st_t *rpz_st; /* RPZ state */
|
||||
dns_zone_t *zone; /* zone to search */
|
||||
|
||||
isc_result_t result; /* query result */
|
||||
int line; /* line to report error */
|
||||
} query_ctx_t;
|
||||
|
||||
isc_result_t
|
||||
ns_query_init(ns_client_t *client);
|
||||
|
||||
@@ -104,4 +154,16 @@ ns_query_start(ns_client_t *client);
|
||||
void
|
||||
ns_query_cancel(ns_client_t *client);
|
||||
|
||||
/*%
|
||||
* (Must not be used outside this module and its associated unit tests.)
|
||||
*/
|
||||
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);
|
||||
|
||||
#endif /* NS_QUERY_H */
|
||||
|
167
lib/ns/query.c
167
lib/ns/query.c
@@ -62,6 +62,8 @@
|
||||
#include <ns/stats.h>
|
||||
#include <ns/xfrout.h>
|
||||
|
||||
#include "hooks.h"
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* It has been recommended that DNS64 be changed to return excluded
|
||||
@@ -243,17 +245,32 @@ static void
|
||||
log_noexistnodata(void *val, int level, const char *fmt, ...)
|
||||
ISC_FORMAT_PRINTF(3, 4);
|
||||
|
||||
/*%
|
||||
* The structure and functions defined below implement the query logic
|
||||
* that previously lived in the single very complex function query_find().
|
||||
* The query_ctx_t structure maintains state from function to function.
|
||||
* The call flow for the general query processing algorithm is described
|
||||
* below:
|
||||
#ifdef NS_HOOKS_ENABLE
|
||||
|
||||
LIBNS_EXTERNAL_DATA const ns_hook_t *ns__hook_table = NULL;
|
||||
|
||||
#define PROCESS_HOOK(...) \
|
||||
NS_PROCESS_HOOK(ns__hook_table, __VA_ARGS__)
|
||||
#define PROCESS_HOOK_VOID(...) \
|
||||
NS_PROCESS_HOOK_VOID(ns__hook_table, __VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define PROCESS_HOOK(...) do {} while (0)
|
||||
#define PROCESS_HOOK_VOID(...) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The functions defined below implement the query logic that previously lived
|
||||
* in the single very complex function query_find(). The query_ctx_t structure
|
||||
* defined in <ns/query.h> maintains state from function to function. The call
|
||||
* flow for the general query processing algorithm is described below:
|
||||
*
|
||||
* 1. Set up query context and other resources for a client
|
||||
* query (query_setup())
|
||||
*
|
||||
* 2. Start the search (query_start())
|
||||
* 2. Start the search (ns__query_start())
|
||||
*
|
||||
* 3. Identify authoritative data sources which may have an answer;
|
||||
* search them (query_lookup()). If an answer is found, go to 7.
|
||||
@@ -310,53 +327,6 @@ log_noexistnodata(void *val, int level, const char *fmt, ...)
|
||||
* DNS64, filter-aaaa, RPZ, RRL, and the SERVFAIL cache.)
|
||||
*/
|
||||
|
||||
typedef struct query_ctx {
|
||||
isc_buffer_t *dbuf; /* name buffer */
|
||||
dns_name_t *fname; /* found name from DB lookup */
|
||||
dns_rdataset_t *rdataset; /* found rdataset */
|
||||
dns_rdataset_t *sigrdataset; /* found sigrdataset */
|
||||
dns_rdataset_t *noqname; /* rdataset needing
|
||||
* NOQNAME proof */
|
||||
dns_rdatatype_t qtype;
|
||||
dns_rdatatype_t type;
|
||||
|
||||
unsigned int options; /* DB lookup options */
|
||||
|
||||
isc_boolean_t redirected; /* nxdomain redirected? */
|
||||
isc_boolean_t is_zone; /* is DB a zone DB? */
|
||||
isc_boolean_t is_staticstub_zone;
|
||||
isc_boolean_t resuming; /* resumed from recursion? */
|
||||
isc_boolean_t dns64, dns64_exclude, rpz;
|
||||
isc_boolean_t authoritative; /* authoritative query? */
|
||||
isc_boolean_t want_restart; /* CNAME chain or other
|
||||
* restart needed */
|
||||
isc_boolean_t need_wildcardproof; /* wilcard proof needed */
|
||||
isc_boolean_t nxrewrite; /* negative answer from RPZ */
|
||||
isc_boolean_t findcoveringnsec; /* lookup covering NSEC */
|
||||
isc_boolean_t want_stale; /* want stale records? */
|
||||
dns_fixedname_t wildcardname; /* name needing wcard proof */
|
||||
dns_fixedname_t dsname; /* name needing DS */
|
||||
|
||||
ns_client_t *client; /* client object */
|
||||
dns_fetchevent_t *event; /* recursion event */
|
||||
|
||||
dns_db_t *db; /* zone or cache database */
|
||||
dns_dbversion_t *version; /* DB version */
|
||||
dns_dbnode_t *node; /* DB node */
|
||||
|
||||
dns_db_t *zdb; /* zone DB values, saved */
|
||||
dns_name_t *zfname; /* while searching cache */
|
||||
dns_dbversion_t *zversion; /* for a better answer */
|
||||
dns_rdataset_t *zrdataset;
|
||||
dns_rdataset_t *zsigrdataset;
|
||||
|
||||
dns_rpz_st_t *rpz_st; /* RPZ state */
|
||||
dns_zone_t *zone; /* zone to search */
|
||||
|
||||
isc_result_t result; /* query result */
|
||||
int line; /* line to report error */
|
||||
} query_ctx_t;
|
||||
|
||||
static void
|
||||
query_trace(query_ctx_t *qctx);
|
||||
|
||||
@@ -367,9 +337,6 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
|
||||
static isc_result_t
|
||||
query_setup(ns_client_t *client, dns_rdatatype_t qtype);
|
||||
|
||||
static isc_result_t
|
||||
query_start(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_lookup(query_ctx_t *qctx);
|
||||
|
||||
@@ -384,9 +351,6 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
|
||||
static isc_result_t
|
||||
query_resume(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_sfcache(query_ctx_t *qctx);
|
||||
|
||||
static isc_result_t
|
||||
query_checkrrl(query_ctx_t *qctx, isc_result_t result);
|
||||
|
||||
@@ -5121,12 +5085,12 @@ query_trace(query_ctx_t *qctx) {
|
||||
/*
|
||||
* Set up query processing for the current query of 'client'.
|
||||
* Calls qctx_init() to initialize a query context, checks
|
||||
* the SERVFAIL cache, then hands off processing to query_start().
|
||||
* the SERVFAIL cache, then hands off processing to ns__query_start().
|
||||
*
|
||||
* This is called only from ns_query_start(), to begin a query
|
||||
* for the first time. Restarting an existing query (for
|
||||
* instance, to handle CNAME lookups), is done by calling
|
||||
* query_start() again with the same query context. Resuming from
|
||||
* ns__query_start() again with the same query context. Resuming from
|
||||
* recursion is handled by query_resume().
|
||||
*/
|
||||
static isc_result_t
|
||||
@@ -5137,14 +5101,6 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) {
|
||||
qctx_init(client, NULL, qtype, &qctx);
|
||||
query_trace(&qctx);
|
||||
|
||||
/*
|
||||
* Check SERVFAIL cache
|
||||
*/
|
||||
result = query_sfcache(&qctx);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's a SIG query, we'll iterate the node.
|
||||
*/
|
||||
@@ -5152,11 +5108,19 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) {
|
||||
qctx.qtype == dns_rdatatype_sig)
|
||||
{
|
||||
qctx.type = dns_rdatatype_any;
|
||||
} else {
|
||||
qctx.type = qctx.qtype;
|
||||
}
|
||||
|
||||
return (query_start(&qctx));
|
||||
PROCESS_HOOK(NS_QUERY_SETUP_QCTX_INITIALIZED, &qctx);
|
||||
|
||||
/*
|
||||
* Check SERVFAIL cache
|
||||
*/
|
||||
result = ns__query_sfcache(&qctx);
|
||||
if (result != ISC_R_COMPLETE) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
return (ns__query_start(&qctx));
|
||||
}
|
||||
|
||||
/*%
|
||||
@@ -5166,10 +5130,10 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) {
|
||||
* follow a CNAME chain. Determines which authoritative database to
|
||||
* search, then hands off processing to query_lookup().
|
||||
*/
|
||||
static isc_result_t
|
||||
query_start(query_ctx_t *qctx) {
|
||||
isc_result_t
|
||||
ns__query_start(query_ctx_t *qctx) {
|
||||
isc_result_t result;
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "query_start");
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "ns__query_start");
|
||||
qctx->want_restart = ISC_FALSE;
|
||||
qctx->authoritative = ISC_FALSE;
|
||||
qctx->version = NULL;
|
||||
@@ -5206,6 +5170,12 @@ query_start(query_ctx_t *qctx) {
|
||||
if (dns_rdatatype_atparent(qctx->qtype) &&
|
||||
!dns_name_equal(qctx->client->query.qname, dns_rootname))
|
||||
{
|
||||
/*
|
||||
* If authoritative data for this QTYPE is supposed to live in
|
||||
* the parent zone, do not look for an exact match for QNAME,
|
||||
* but rather for its containing zone (unless the QNAME is
|
||||
* root).
|
||||
*/
|
||||
qctx->options |= DNS_GETDB_NOEXACT;
|
||||
}
|
||||
|
||||
@@ -5218,8 +5188,10 @@ query_start(query_ctx_t *qctx) {
|
||||
(qctx->options & DNS_GETDB_NOEXACT) != 0))
|
||||
{
|
||||
/*
|
||||
* If the query type is DS, look to see if we are
|
||||
* authoritative for the child zone.
|
||||
* This is a non-recursive QTYPE=DS query with QNAME whose
|
||||
* parent we are not authoritative for. Check whether we are
|
||||
* authoritative for QNAME, because if so, we need to send a
|
||||
* "no data" response as required by RFC 4035, section 3.1.4.1.
|
||||
*/
|
||||
dns_db_t *tdb = NULL;
|
||||
dns_zone_t *tzone = NULL;
|
||||
@@ -5232,6 +5204,10 @@ query_start(query_ctx_t *qctx) {
|
||||
DNS_GETDB_PARTIAL,
|
||||
&tzone, &tdb, &tversion);
|
||||
if (tresult == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* We are authoritative for QNAME. Attach the relevant
|
||||
* zone to query context, set result to ISC_R_SUCCESS.
|
||||
*/
|
||||
qctx->options &= ~DNS_GETDB_NOEXACT;
|
||||
query_putrdataset(qctx->client, &qctx->rdataset);
|
||||
if (qctx->db != NULL) {
|
||||
@@ -5247,6 +5223,10 @@ query_start(query_ctx_t *qctx) {
|
||||
qctx->is_zone = ISC_TRUE;
|
||||
result = ISC_R_SUCCESS;
|
||||
} else {
|
||||
/*
|
||||
* We are not authoritative for QNAME. Clean up and
|
||||
* leave result as it was.
|
||||
*/
|
||||
if (tdb != NULL) {
|
||||
dns_db_detach(&tdb);
|
||||
}
|
||||
@@ -5255,6 +5235,11 @@ query_start(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we did not find a database from which we can answer the query,
|
||||
* respond with either REFUSED or SERVFAIL, depending on what the
|
||||
* result of query_getdb() was.
|
||||
*/
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
if (result == DNS_R_REFUSED) {
|
||||
if (WANTRECURSION(qctx->client)) {
|
||||
@@ -5269,12 +5254,17 @@ query_start(query_ctx_t *qctx) {
|
||||
}
|
||||
} else {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"query_start: query_getdb failed");
|
||||
"ns__query_start: query_getdb failed");
|
||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||
}
|
||||
return (query_done(qctx));
|
||||
}
|
||||
|
||||
/*
|
||||
* We found a database from which we can answer the query. Update
|
||||
* relevant query context flags if the answer is to be prepared using
|
||||
* authoritative data.
|
||||
*/
|
||||
qctx->is_staticstub_zone = ISC_FALSE;
|
||||
if (qctx->is_zone) {
|
||||
qctx->authoritative = ISC_TRUE;
|
||||
@@ -5285,6 +5275,10 @@ query_start(query_ctx_t *qctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach to the database which will be used to prepare the answer.
|
||||
* Update query statistics.
|
||||
*/
|
||||
if (qctx->event == NULL && qctx->client->query.restarts == 0) {
|
||||
if (qctx->is_zone) {
|
||||
if (qctx->zone != NULL) {
|
||||
@@ -5326,6 +5320,8 @@ query_lookup(query_ctx_t *qctx) {
|
||||
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
|
||||
|
||||
PROCESS_HOOK(NS_QUERY_LOOKUP_BEGIN, qctx);
|
||||
|
||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||
dns_clientinfo_init(&ci, qctx->client, NULL);
|
||||
|
||||
@@ -5884,10 +5880,11 @@ query_resume(query_ctx_t *qctx) {
|
||||
* If the query is recursive, check the SERVFAIL cache to see whether
|
||||
* identical queries have failed recently. If we find a match, and it was
|
||||
* from a query with CD=1, *or* if the current query has CD=0, then we just
|
||||
* return SERVFAIL again.
|
||||
* return SERVFAIL again. This prevents a validation failure from eliciting a
|
||||
* SERVFAIL response to a CD=1 query.
|
||||
*/
|
||||
static isc_result_t
|
||||
query_sfcache(query_ctx_t *qctx) {
|
||||
isc_result_t
|
||||
ns__query_sfcache(query_ctx_t *qctx) {
|
||||
isc_boolean_t failcache;
|
||||
isc_uint32_t flags;
|
||||
|
||||
@@ -10317,7 +10314,7 @@ query_glueanswer(query_ctx_t *qctx) {
|
||||
*
|
||||
* - Clean up
|
||||
* - If we have an answer ready (positive or negative), send it.
|
||||
* - If we need to restart for a chaining query, call query_start() again.
|
||||
* - 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().
|
||||
*/
|
||||
@@ -10326,6 +10323,8 @@ query_done(query_ctx_t *qctx) {
|
||||
const dns_namelist_t *secs = qctx->client->message->sections;
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "query_done");
|
||||
|
||||
PROCESS_HOOK(NS_QUERY_DONE_BEGIN, qctx);
|
||||
|
||||
/*
|
||||
* General cleanup.
|
||||
*/
|
||||
@@ -10352,7 +10351,7 @@ query_done(query_ctx_t *qctx) {
|
||||
*/
|
||||
if (qctx->want_restart && qctx->client->query.restarts < MAX_RESTARTS) {
|
||||
qctx->client->query.restarts++;
|
||||
return (query_start(qctx));
|
||||
return (ns__query_start(qctx));
|
||||
}
|
||||
|
||||
if (qctx->want_stale) {
|
||||
|
@@ -32,11 +32,13 @@ LIBS = @LIBS@ @ATFLIBS@
|
||||
OBJS = nstest.@O@
|
||||
SRCS = nstest.c \
|
||||
listenlist_test.c \
|
||||
notify_test.c
|
||||
notify_test.c \
|
||||
query_test.c
|
||||
|
||||
SUBDIRS =
|
||||
TARGETS = listenlist_test@EXEEXT@ \
|
||||
notify_test@EXEEXT@
|
||||
notify_test@EXEEXT@ \
|
||||
query_test
|
||||
|
||||
@BIND9_MAKE_RULES@
|
||||
|
||||
@@ -50,6 +52,11 @@ notify_test@EXEEXT@: notify_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNS
|
||||
notify_test.@O@ nstest.@O@ ${NSLIBS} ${DNSLIBS} \
|
||||
${ISCLIBS} ${LIBS}
|
||||
|
||||
query_test@EXEEXT@: query_test.@O@ nstest.@O@ ${NSDEPLIBS} ${ISCDEPLIBS} ${DNSDEPLIBS}
|
||||
${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
|
||||
query_test.@O@ nstest.@O@ ${NSLIBS} ${DNSLIBS} \
|
||||
${ISCLIBS} ${LIBS}
|
||||
|
||||
unit::
|
||||
sh ${top_srcdir}/unit/unittest.sh
|
||||
|
||||
|
@@ -28,49 +28,6 @@
|
||||
|
||||
#include "nstest.h"
|
||||
|
||||
static dns_zone_t *zone = NULL;
|
||||
static dns_view_t *view = NULL;
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
static void
|
||||
setup_zone(const char *zonename, const char *filename) {
|
||||
isc_result_t result;
|
||||
dns_db_t *db = NULL;
|
||||
|
||||
result = ns_test_makezone(zonename, &zone, NULL, ISC_TRUE);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
result = ns_test_setupzonemgr();
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
result = ns_test_managezone(zone);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
view = dns_zone_getview(zone);
|
||||
ATF_REQUIRE(view->zonetable != NULL);
|
||||
view->nocookieudp = 512;
|
||||
|
||||
dns_zone_setfile(zone, filename);
|
||||
result = dns_zone_load(zone);
|
||||
ATF_REQUIRE(result == ISC_R_SUCCESS);
|
||||
|
||||
/* The zone should now be loaded; test it */
|
||||
result = dns_zone_getdb(zone, &db);
|
||||
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
|
||||
ATF_CHECK(db != NULL);
|
||||
if (db != NULL) {
|
||||
dns_db_detach(&db);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_zone() {
|
||||
ns_test_releasezone(zone);
|
||||
ns_test_closezonemgr();
|
||||
|
||||
dns_zone_detach(&zone);
|
||||
dns_view_detach(&view);
|
||||
}
|
||||
|
||||
static void
|
||||
check_response(isc_buffer_t *buf) {
|
||||
isc_result_t result;
|
||||
@@ -113,7 +70,12 @@ ATF_TC_BODY(notify_start, tc) {
|
||||
result = ns_test_getclient(NULL, ISC_FALSE, &client);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
setup_zone("example.com", "testdata/notify/zone1.db");
|
||||
result = ns_test_makeview("view", ISC_FALSE, &client->view);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
result = ns_test_serve_zone("example.com", "testdata/notify/zone1.db",
|
||||
client->view);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
/*
|
||||
* Create a NOTIFY message by parsing a file in testdata.
|
||||
@@ -136,7 +98,6 @@ ATF_TC_BODY(notify_start, tc) {
|
||||
* Set up client object with this message and test the NOTIFY
|
||||
* handler.
|
||||
*/
|
||||
dns_view_attach(view, &client->view);
|
||||
if (client->message != NULL) {
|
||||
dns_message_destroy(&client->message);
|
||||
}
|
||||
@@ -148,7 +109,7 @@ ATF_TC_BODY(notify_start, tc) {
|
||||
/*
|
||||
* Clean up
|
||||
*/
|
||||
cleanup_zone();
|
||||
ns_test_cleanup_zone();
|
||||
|
||||
ns_client_detach(&client);
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <isc/mem.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/print.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/string.h>
|
||||
#include <isc/socket.h>
|
||||
#include <isc/stdio.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <isc/timer.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include <dns/cache.h>
|
||||
#include <dns/db.h>
|
||||
#include <dns/dispatch.h>
|
||||
#include <dns/fixedname.h>
|
||||
@@ -42,6 +44,8 @@
|
||||
#include <ns/interfacemgr.h>
|
||||
#include <ns/server.h>
|
||||
|
||||
#include "../hooks.h"
|
||||
|
||||
#include "nstest.h"
|
||||
|
||||
isc_mem_t *mctx = NULL;
|
||||
@@ -63,6 +67,8 @@ isc_boolean_t run_managers = ISC_FALSE;
|
||||
|
||||
static isc_boolean_t hash_active = ISC_FALSE, dst_active = ISC_FALSE;
|
||||
|
||||
static dns_zone_t *served_zone = NULL;
|
||||
|
||||
/*
|
||||
* Logging categories: this needs to match the list in lib/ns/log.c.
|
||||
*/
|
||||
@@ -275,6 +281,8 @@ ns_test_begin(FILE *logfile, isc_boolean_t start_managers) {
|
||||
if (chdir(TESTS) == -1)
|
||||
CHECK(ISC_R_FAILURE);
|
||||
|
||||
ns__hook_table = NULL;
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
cleanup:
|
||||
@@ -306,15 +314,29 @@ ns_test_end(void) {
|
||||
isc_mem_destroy(&mctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a view.
|
||||
*/
|
||||
isc_result_t
|
||||
ns_test_makeview(const char *name, dns_view_t **viewp) {
|
||||
isc_result_t result;
|
||||
ns_test_makeview(const char *name, isc_boolean_t with_cache,
|
||||
dns_view_t **viewp)
|
||||
{
|
||||
dns_cache_t *cache = NULL;
|
||||
dns_view_t *view = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
CHECK(dns_view_create(mctx, dns_rdataclass_in, name, &view));
|
||||
|
||||
if (with_cache) {
|
||||
CHECK(dns_cache_create(mctx, taskmgr, timermgr,
|
||||
dns_rdataclass_in, "rbt", 0, NULL,
|
||||
&cache));
|
||||
dns_view_setcache(view, cache);
|
||||
/*
|
||||
* Reference count for "cache" is now at 2, so decrement it in
|
||||
* order for the cache to be automatically freed when "view"
|
||||
* gets freed.
|
||||
*/
|
||||
dns_cache_detach(&cache);
|
||||
}
|
||||
|
||||
*viewp = view;
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
@@ -417,6 +439,79 @@ ns_test_closezonemgr(void) {
|
||||
dns_zonemgr_detach(&zonemgr);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
ns_test_serve_zone(const char *zonename, const char *filename,
|
||||
dns_view_t *view)
|
||||
{
|
||||
isc_result_t result;
|
||||
dns_db_t *db = NULL;
|
||||
|
||||
/*
|
||||
* Prepare zone structure for further processing.
|
||||
*/
|
||||
result = ns_test_makezone(zonename, &served_zone, view, ISC_TRUE);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start zone manager.
|
||||
*/
|
||||
result = ns_test_setupzonemgr();
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto free_zone;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the zone to the zone manager.
|
||||
*/
|
||||
result = ns_test_managezone(served_zone);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto close_zonemgr;
|
||||
}
|
||||
|
||||
view->nocookieudp = 512;
|
||||
|
||||
/*
|
||||
* Set path to the master file for the zone and then load it.
|
||||
*/
|
||||
dns_zone_setfile(served_zone, filename);
|
||||
result = dns_zone_load(served_zone);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto release_zone;
|
||||
}
|
||||
|
||||
/*
|
||||
* The zone should now be loaded; test it.
|
||||
*/
|
||||
result = dns_zone_getdb(served_zone, &db);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto release_zone;
|
||||
}
|
||||
if (db != NULL) {
|
||||
dns_db_detach(&db);
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
release_zone:
|
||||
ns_test_releasezone(served_zone);
|
||||
close_zonemgr:
|
||||
ns_test_closezonemgr();
|
||||
free_zone:
|
||||
dns_zone_detach(&served_zone);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
ns_test_cleanup_zone(void) {
|
||||
ns_test_releasezone(served_zone);
|
||||
ns_test_closezonemgr();
|
||||
|
||||
dns_zone_detach(&served_zone);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
||||
ns_client_t **clientp)
|
||||
@@ -435,6 +530,291 @@ ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Synthesize a DNS message based on supplied QNAME, QTYPE and flags, then
|
||||
* parse it and store the results in client->message.
|
||||
*/
|
||||
static isc_result_t
|
||||
attach_query_msg_to_client(ns_client_t *client, const char *qnamestr,
|
||||
dns_rdatatype_t qtype, unsigned int qflags)
|
||||
{
|
||||
dns_rdataset_t *qrdataset = NULL;
|
||||
dns_message_t *message = NULL;
|
||||
unsigned char query[65536];
|
||||
dns_name_t *qname = NULL;
|
||||
isc_buffer_t querybuf;
|
||||
dns_compress_t cctx;
|
||||
isc_result_t result;
|
||||
isc_uint32_t qid;
|
||||
|
||||
REQUIRE(client != NULL);
|
||||
REQUIRE(qnamestr != NULL);
|
||||
|
||||
/*
|
||||
* Create a new DNS message holding a query.
|
||||
*/
|
||||
result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set query ID to a random value.
|
||||
*/
|
||||
isc_random_get(&qid);
|
||||
message->id = (dns_messageid_t)(qid & 0xffff);
|
||||
|
||||
/*
|
||||
* Set query flags as requested by the caller.
|
||||
*/
|
||||
message->flags = qflags;
|
||||
|
||||
/*
|
||||
* Allocate structures required to construct the query.
|
||||
*/
|
||||
result = dns_message_gettemprdataset(message, &qrdataset);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto destroy_message;
|
||||
}
|
||||
result = dns_message_gettempname(message, &qname);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto put_rdataset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert "qnamestr" to a DNS name, create a question rdataset of
|
||||
* class IN and type "qtype", link the two and add the result to the
|
||||
* QUESTION section of the query.
|
||||
*/
|
||||
result = dns_name_fromstring(qname, qnamestr, 0, mctx);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto put_name;
|
||||
}
|
||||
dns_rdataset_makequestion(qrdataset, dns_rdataclass_in, qtype);
|
||||
ISC_LIST_APPEND(qname->list, qrdataset, link);
|
||||
dns_message_addname(message, qname, DNS_SECTION_QUESTION);
|
||||
|
||||
/*
|
||||
* Render the query.
|
||||
*/
|
||||
dns_compress_init(&cctx, -1, mctx);
|
||||
isc_buffer_init(&querybuf, query, sizeof(query));
|
||||
result = dns_message_renderbegin(message, &cctx, &querybuf);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto destroy_message;
|
||||
}
|
||||
result = dns_message_rendersection(message, DNS_SECTION_QUESTION, 0);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto destroy_message;
|
||||
}
|
||||
result = dns_message_renderend(message);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto destroy_message;
|
||||
}
|
||||
dns_compress_invalidate(&cctx);
|
||||
|
||||
/*
|
||||
* Destroy the created message as it was rendered into "querybuf" and
|
||||
* the latter is all we are going to need from now on.
|
||||
*/
|
||||
dns_message_destroy(&message);
|
||||
|
||||
/*
|
||||
* Parse the rendered query, storing results in client->message.
|
||||
*/
|
||||
isc_buffer_first(&querybuf);
|
||||
return (dns_message_parse(client->message, &querybuf, 0));
|
||||
|
||||
put_name:
|
||||
dns_message_puttempname(message, &qname);
|
||||
put_rdataset:
|
||||
dns_message_puttemprdataset(message, &qrdataset);
|
||||
destroy_message:
|
||||
dns_message_destroy(&message);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*%
|
||||
* A hook callback which stores the query context pointed to by "hook_data" at
|
||||
* "callback_data". Causes execution to be interrupted at hook insertion
|
||||
* point.
|
||||
*/
|
||||
static isc_boolean_t
|
||||
extract_qctx(void *hook_data, void *callback_data, isc_result_t *resultp) {
|
||||
query_ctx_t **qctxp;
|
||||
query_ctx_t *qctx;
|
||||
|
||||
REQUIRE(hook_data != NULL);
|
||||
REQUIRE(callback_data != NULL);
|
||||
REQUIRE(resultp != NULL);
|
||||
|
||||
/*
|
||||
* qctx is a stack variable in lib/ns/query.c. Its contents need to be
|
||||
* duplicated or otherwise they will become invalidated once the stack
|
||||
* gets unwound.
|
||||
*/
|
||||
qctx = isc_mem_get(mctx, sizeof(*qctx));
|
||||
if (qctx != NULL) {
|
||||
memmove(qctx, (query_ctx_t *)hook_data, sizeof(*qctx));
|
||||
}
|
||||
|
||||
qctxp = (query_ctx_t **)callback_data;
|
||||
/*
|
||||
* If memory allocation failed, the supplied pointer will simply be set
|
||||
* to NULL. We rely on the user of this hook to react properly.
|
||||
*/
|
||||
*qctxp = qctx;
|
||||
*resultp = ISC_R_UNSET;
|
||||
|
||||
return (ISC_TRUE);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Initialize a query context for "client" and store it in "qctxp".
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
* \li "client->message" to hold a parsed DNS query.
|
||||
*/
|
||||
static isc_result_t
|
||||
create_qctx_for_client(ns_client_t *client, query_ctx_t **qctxp) {
|
||||
const ns_hook_t *saved_hook_table;
|
||||
const ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT] = {
|
||||
[NS_QUERY_SETUP_QCTX_INITIALIZED] = {
|
||||
.callback = extract_qctx,
|
||||
.callback_data = qctxp,
|
||||
},
|
||||
};
|
||||
|
||||
REQUIRE(client != NULL);
|
||||
REQUIRE(qctxp != NULL);
|
||||
REQUIRE(*qctxp == NULL);
|
||||
|
||||
/*
|
||||
* Call ns_query_start() to initialize a query context for given
|
||||
* client, but first hook into query_setup() so that we can just
|
||||
* extract an initialized query context, without kicking off any
|
||||
* further processing. Make sure we do not overwrite any previously
|
||||
* set hooks.
|
||||
*/
|
||||
saved_hook_table = ns__hook_table;
|
||||
ns__hook_table = query_hooks;
|
||||
ns_query_start(client);
|
||||
ns__hook_table = saved_hook_table;
|
||||
|
||||
if (*qctxp == NULL) {
|
||||
return (ISC_R_NOMEMORY);
|
||||
} else {
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
ns_test_qctx_create(const ns_test_qctx_create_params_t *params,
|
||||
query_ctx_t **qctxp)
|
||||
{
|
||||
ns_client_t *client = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(params != NULL);
|
||||
REQUIRE(params->qname != NULL);
|
||||
REQUIRE(qctxp != NULL);
|
||||
REQUIRE(*qctxp == NULL);
|
||||
|
||||
/*
|
||||
* Allocate and initialize a client structure.
|
||||
*/
|
||||
result = ns_test_getclient(NULL, ISC_FALSE, &client);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
TIME_NOW(&client->tnow);
|
||||
|
||||
/*
|
||||
* Every client needs to belong to a view.
|
||||
*/
|
||||
result = ns_test_makeview("view", params->with_cache, &client->view);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto detach_client;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synthesize a DNS query using given QNAME, QTYPE and flags, storing
|
||||
* it in client->message.
|
||||
*/
|
||||
result = attach_query_msg_to_client(client, params->qname,
|
||||
params->qtype, params->qflags);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto detach_client;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow recursion for the client. As NS_CLIENTATTR_RA normally gets
|
||||
* set in ns__client_request(), i.e. earlier than the unit tests hook
|
||||
* into the call chain, just set it manually.
|
||||
*/
|
||||
client->attributes |= NS_CLIENTATTR_RA;
|
||||
|
||||
/*
|
||||
* Create a query context for a client sending the previously
|
||||
* synthesized query.
|
||||
*/
|
||||
result = create_qctx_for_client(client, qctxp);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
goto destroy_query;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reference count for "client" is now at 2, so decrement it in order
|
||||
* for it to drop to zero when "qctx" gets destroyed.
|
||||
*/
|
||||
ns_client_detach(&client);
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
destroy_query:
|
||||
dns_message_destroy(&client->message);
|
||||
detach_client:
|
||||
ns_client_detach(&client);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
ns_test_qctx_destroy(query_ctx_t **qctxp) {
|
||||
query_ctx_t *qctx;
|
||||
|
||||
REQUIRE(qctxp != NULL);
|
||||
REQUIRE(*qctxp != NULL);
|
||||
|
||||
qctx = *qctxp;
|
||||
|
||||
ns_client_detach(&qctx->client);
|
||||
|
||||
if (qctx->zone != NULL) {
|
||||
dns_zone_detach(&qctx->zone);
|
||||
}
|
||||
if (qctx->db != NULL) {
|
||||
dns_db_detach(&qctx->db);
|
||||
}
|
||||
|
||||
isc_mem_put(mctx, qctx, sizeof(*qctx));
|
||||
*qctxp = NULL;
|
||||
}
|
||||
|
||||
isc_boolean_t
|
||||
ns_test_hook_catch_call(void *hook_data, void *callback_data,
|
||||
isc_result_t *resultp)
|
||||
{
|
||||
UNUSED(hook_data);
|
||||
UNUSED(callback_data);
|
||||
|
||||
*resultp = ISC_R_UNSET;
|
||||
|
||||
return (ISC_TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sleep for 'usec' microseconds.
|
||||
*/
|
||||
|
@@ -26,6 +26,13 @@
|
||||
#include <ns/interfacemgr.h>
|
||||
#include <ns/client.h>
|
||||
|
||||
typedef struct ns_test_id {
|
||||
const char *description;
|
||||
int lineno;
|
||||
} ns_test_id_t;
|
||||
|
||||
#define NS_TEST_ID(desc) { .description = desc, .lineno = __LINE__ }
|
||||
|
||||
#define CHECK(r) \
|
||||
do { \
|
||||
result = (r); \
|
||||
@@ -55,8 +62,13 @@ ns_test_begin(FILE *logfile, isc_boolean_t create_managers);
|
||||
void
|
||||
ns_test_end(void);
|
||||
|
||||
/*%
|
||||
* Create a view. If "with_cache" is set to ISC_TRUE, a cache database will
|
||||
* also be created and attached to the created view.
|
||||
*/
|
||||
isc_result_t
|
||||
ns_test_makeview(const char *name, dns_view_t **viewp);
|
||||
ns_test_makeview(const char *name, isc_boolean_t with_cache,
|
||||
dns_view_t **viewp);
|
||||
|
||||
isc_result_t
|
||||
ns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
|
||||
@@ -74,6 +86,21 @@ ns_test_releasezone(dns_zone_t *zone);
|
||||
void
|
||||
ns_test_closezonemgr(void);
|
||||
|
||||
/*%
|
||||
* Load data for zone "zonename" from file "filename" and start serving it to
|
||||
* clients matching "view". Only one zone loaded using this function can be
|
||||
* served at any given time.
|
||||
*/
|
||||
isc_result_t
|
||||
ns_test_serve_zone(const char *zonename, const char *filename,
|
||||
dns_view_t *view);
|
||||
|
||||
/*%
|
||||
* Release the zone loaded by ns_test_serve_zone().
|
||||
*/
|
||||
void
|
||||
ns_test_cleanup_zone(void);
|
||||
|
||||
void
|
||||
ns_test_nap(isc_uint32_t usec);
|
||||
|
||||
@@ -88,3 +115,37 @@ ns_test_getdata(const char *file, unsigned char *buf,
|
||||
isc_result_t
|
||||
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
||||
ns_client_t **clientp);
|
||||
|
||||
/*%
|
||||
* Structure containing parameters for ns_test_qctx_create().
|
||||
*/
|
||||
typedef struct ns_test_qctx_create_params {
|
||||
const char *qname;
|
||||
dns_rdatatype_t qtype;
|
||||
unsigned int qflags;
|
||||
isc_boolean_t with_cache;
|
||||
} ns_test_qctx_create_params_t;
|
||||
|
||||
/*%
|
||||
* Prepare a query context identical with one that would be prepared if a query
|
||||
* with given QNAME, QTYPE and flags was received from a client. Recursion is
|
||||
* assumed to be allowed for this client. If "with_cache" is set to ISC_TRUE,
|
||||
* a cache database will be created and associated with the view matching the
|
||||
* incoming query.
|
||||
*/
|
||||
isc_result_t
|
||||
ns_test_qctx_create(const ns_test_qctx_create_params_t *params,
|
||||
query_ctx_t **qctxp);
|
||||
|
||||
/*%
|
||||
* Destroy a query context created by ns_test_qctx_create().
|
||||
*/
|
||||
void
|
||||
ns_test_qctx_destroy(query_ctx_t **qctxp);
|
||||
|
||||
/*%
|
||||
* A hook callback interrupting execution at given hook's insertion point.
|
||||
*/
|
||||
isc_boolean_t
|
||||
ns_test_hook_catch_call(void *hook_data, void *callback_data,
|
||||
isc_result_t *resultp);
|
||||
|
574
lib/ns/tests/query_test.c
Normal file
574
lib/ns/tests/query_test.c
Normal file
@@ -0,0 +1,574 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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/.
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
#include <dns/badcache.h>
|
||||
#include <dns/view.h>
|
||||
#include <isc/util.h>
|
||||
#include <ns/client.h>
|
||||
#include <ns/query.h>
|
||||
|
||||
#include "../hooks.h"
|
||||
|
||||
#include "nstest.h"
|
||||
|
||||
/*****
|
||||
***** ns__query_sfcache() tests
|
||||
*****/
|
||||
|
||||
/*%
|
||||
* Structure containing parameters for ns__query_sfcache_test().
|
||||
*/
|
||||
typedef struct {
|
||||
const ns_test_id_t id; /* libns test identifier */
|
||||
unsigned int qflags; /* query flags */
|
||||
isc_boolean_t cache_entry_present; /* whether a SERVFAIL cache entry
|
||||
matching the query should be
|
||||
present */
|
||||
isc_uint32_t cache_entry_flags; /* NS_FAILCACHE_* flags to set for
|
||||
the SERVFAIL cache entry */
|
||||
isc_boolean_t servfail_expected; /* whether a cached SERVFAIL is
|
||||
expected to be returned */
|
||||
} ns__query_sfcache_test_params_t;
|
||||
|
||||
/*%
|
||||
* Perform a single ns__query_sfcache() check using given parameters.
|
||||
*/
|
||||
static void
|
||||
ns__query_sfcache_test(const ns__query_sfcache_test_params_t *test) {
|
||||
query_ctx_t *qctx = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(test != NULL);
|
||||
REQUIRE(test->id.description != NULL);
|
||||
REQUIRE(test->cache_entry_present == ISC_TRUE ||
|
||||
test->cache_entry_flags == 0);
|
||||
|
||||
/*
|
||||
* Interrupt execution if query_done() is called.
|
||||
*/
|
||||
const ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT] = {
|
||||
[NS_QUERY_DONE_BEGIN] = {
|
||||
.callback = ns_test_hook_catch_call,
|
||||
.callback_data = NULL,
|
||||
},
|
||||
};
|
||||
ns__hook_table = query_hooks;
|
||||
|
||||
/*
|
||||
* Construct a query context for a ./NS query with given flags.
|
||||
*/
|
||||
{
|
||||
const ns_test_qctx_create_params_t qctx_params = {
|
||||
.qname = ".",
|
||||
.qtype = dns_rdatatype_ns,
|
||||
.qflags = test->qflags,
|
||||
.with_cache = ISC_TRUE,
|
||||
};
|
||||
|
||||
result = ns_test_qctx_create(&qctx_params, &qctx);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this test wants a SERVFAIL cache entry matching the query to
|
||||
* exist, create it.
|
||||
*/
|
||||
if (test->cache_entry_present) {
|
||||
isc_interval_t hour;
|
||||
isc_time_t expire;
|
||||
|
||||
isc_interval_set(&hour, 3600, 0);
|
||||
result = isc_time_nowplusinterval(&expire, &hour);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
dns_badcache_add(qctx->client->view->failcache, dns_rootname,
|
||||
dns_rdatatype_ns, ISC_TRUE,
|
||||
test->cache_entry_flags, &expire);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether ns__query_sfcache() behaves as expected.
|
||||
*/
|
||||
ns__query_sfcache(qctx);
|
||||
|
||||
if (test->servfail_expected) {
|
||||
ATF_CHECK_EQ_MSG(qctx->result, DNS_R_SERVFAIL,
|
||||
"test \"%s\" on line %d: "
|
||||
"expected SERVFAIL, got %s",
|
||||
test->id.description, test->id.lineno,
|
||||
isc_result_totext(qctx->result));
|
||||
} else {
|
||||
ATF_CHECK_EQ_MSG(qctx->result, ISC_R_SUCCESS,
|
||||
"test \"%s\" on line %d: "
|
||||
"expected success, got %s",
|
||||
test->id.description, test->id.lineno,
|
||||
isc_result_totext(qctx->result));
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
ns_test_qctx_destroy(&qctx);
|
||||
|
||||
}
|
||||
|
||||
ATF_TC(ns__query_sfcache);
|
||||
ATF_TC_HEAD(ns__query_sfcache, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "ns__query_sfcache()");
|
||||
}
|
||||
ATF_TC_BODY(ns__query_sfcache, tc) {
|
||||
isc_result_t result;
|
||||
size_t i;
|
||||
|
||||
const ns__query_sfcache_test_params_t tests[] = {
|
||||
/*
|
||||
* Sanity check for an empty SERVFAIL cache.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=1, CD=0; cache: empty"),
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.cache_entry_present = ISC_FALSE,
|
||||
.servfail_expected = ISC_FALSE,
|
||||
},
|
||||
/*
|
||||
* Query: RD=1, CD=0. Cache entry: CD=0. Should SERVFAIL.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=1, CD=0; cache: CD=0"),
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.cache_entry_present = ISC_TRUE,
|
||||
.cache_entry_flags = 0,
|
||||
.servfail_expected = ISC_TRUE,
|
||||
},
|
||||
/*
|
||||
* Query: RD=1, CD=1. Cache entry: CD=0. Should not SERVFAIL:
|
||||
* failed validation should not influence CD=1 queries.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=1, CD=1; cache: CD=0"),
|
||||
.qflags = DNS_MESSAGEFLAG_RD | DNS_MESSAGEFLAG_CD,
|
||||
.cache_entry_present = ISC_TRUE,
|
||||
.cache_entry_flags = 0,
|
||||
.servfail_expected = ISC_FALSE,
|
||||
},
|
||||
/*
|
||||
* Query: RD=1, CD=1. Cache entry: CD=1. Should SERVFAIL:
|
||||
* SERVFAIL responses elicited by CD=1 queries can be
|
||||
* "replayed" for other CD=1 queries during the lifetime of the
|
||||
* SERVFAIL cache entry.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=1, CD=1; cache: CD=1"),
|
||||
.qflags = DNS_MESSAGEFLAG_RD | DNS_MESSAGEFLAG_CD,
|
||||
.cache_entry_present = ISC_TRUE,
|
||||
.cache_entry_flags = NS_FAILCACHE_CD,
|
||||
.servfail_expected = ISC_TRUE,
|
||||
},
|
||||
/*
|
||||
* Query: RD=1, CD=0. Cache entry: CD=1. Should SERVFAIL: if
|
||||
* a CD=1 query elicited a SERVFAIL, a CD=0 query for the same
|
||||
* QNAME and QTYPE will SERVFAIL as well.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=1, CD=0; cache: CD=1"),
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.cache_entry_present = ISC_TRUE,
|
||||
.cache_entry_flags = NS_FAILCACHE_CD,
|
||||
.servfail_expected = ISC_TRUE,
|
||||
},
|
||||
/*
|
||||
* Query: RD=0, CD=0. Cache entry: CD=0. Should not SERVFAIL
|
||||
* despite a matching entry being present as the SERVFAIL cache
|
||||
* should not be consulted for non-recursive queries.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("query: RD=0, CD=0; cache: CD=0"),
|
||||
.qflags = 0,
|
||||
.cache_entry_present = ISC_TRUE,
|
||||
.cache_entry_flags = 0,
|
||||
.servfail_expected = ISC_FALSE,
|
||||
},
|
||||
};
|
||||
|
||||
UNUSED(tc);
|
||||
|
||||
result = ns_test_begin(NULL, ISC_TRUE);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
ns__query_sfcache_test(&tests[i]);
|
||||
}
|
||||
|
||||
ns_test_end();
|
||||
}
|
||||
|
||||
/*****
|
||||
***** ns__query_start() tests
|
||||
*****/
|
||||
|
||||
/*%
|
||||
* Structure containing parameters for ns__query_start_test().
|
||||
*/
|
||||
typedef struct {
|
||||
const ns_test_id_t id; /* libns test identifier */
|
||||
const char *qname; /* QNAME */
|
||||
dns_rdatatype_t qtype; /* QTYPE */
|
||||
unsigned int qflags; /* query flags */
|
||||
isc_boolean_t disable_name_checks; /* if set to ISC_TRUE, owner name
|
||||
checks will be disabled for the
|
||||
view created */
|
||||
isc_boolean_t recursive_service; /* if set to ISC_TRUE, the view
|
||||
created will have a cache
|
||||
database attached */
|
||||
const char *auth_zone_origin; /* origin name of the zone the
|
||||
created view will be
|
||||
authoritative for */
|
||||
const char *auth_zone_path; /* path to load the authoritative
|
||||
zone from */
|
||||
enum { /* expected result: */
|
||||
NS__QUERY_START_R_INVALID,
|
||||
NS__QUERY_START_R_REFUSE, /* query should be REFUSED */
|
||||
NS__QUERY_START_R_CACHE, /* query should be answered from
|
||||
cache */
|
||||
NS__QUERY_START_R_AUTH, /* query should be answered using
|
||||
authoritative data */
|
||||
} expected_result;
|
||||
} ns__query_start_test_params_t;
|
||||
|
||||
/*%
|
||||
* Perform a single ns__query_start() check using given parameters.
|
||||
*/
|
||||
static void
|
||||
ns__query_start_test(const ns__query_start_test_params_t *test) {
|
||||
query_ctx_t *qctx = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
REQUIRE(test != NULL);
|
||||
REQUIRE(test->id.description != NULL);
|
||||
REQUIRE((test->auth_zone_origin == NULL &&
|
||||
test->auth_zone_path == NULL) ||
|
||||
(test->auth_zone_origin != NULL &&
|
||||
test->auth_zone_path != NULL));
|
||||
|
||||
/*
|
||||
* Interrupt execution if query_lookup() or query_done() is called.
|
||||
*/
|
||||
const ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT] = {
|
||||
[NS_QUERY_LOOKUP_BEGIN] = {
|
||||
.callback = ns_test_hook_catch_call,
|
||||
.callback_data = NULL,
|
||||
},
|
||||
[NS_QUERY_DONE_BEGIN] = {
|
||||
.callback = ns_test_hook_catch_call,
|
||||
.callback_data = NULL,
|
||||
},
|
||||
};
|
||||
ns__hook_table = query_hooks;
|
||||
|
||||
/*
|
||||
* Construct a query context using the supplied parameters.
|
||||
*/
|
||||
{
|
||||
const ns_test_qctx_create_params_t qctx_params = {
|
||||
.qname = test->qname,
|
||||
.qtype = test->qtype,
|
||||
.qflags = test->qflags,
|
||||
.with_cache = test->recursive_service,
|
||||
};
|
||||
result = ns_test_qctx_create(&qctx_params, &qctx);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable view->checknames by default, disable if requested.
|
||||
*/
|
||||
qctx->client->view->checknames = !test->disable_name_checks;
|
||||
|
||||
/*
|
||||
* Load zone from file and attach it to the client's view, if
|
||||
* requested.
|
||||
*/
|
||||
if (test->auth_zone_path != NULL) {
|
||||
result = ns_test_serve_zone(test->auth_zone_origin,
|
||||
test->auth_zone_path,
|
||||
qctx->client->view);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether ns__query_start() behaves as expected.
|
||||
*/
|
||||
ns__query_start(qctx);
|
||||
|
||||
switch (test->expected_result) {
|
||||
case NS__QUERY_START_R_REFUSE:
|
||||
ATF_CHECK_EQ_MSG(qctx->result, DNS_R_REFUSED,
|
||||
"test \"%s\" on line %d: "
|
||||
"expected REFUSED, got %s",
|
||||
test->id.description, test->id.lineno,
|
||||
isc_result_totext(qctx->result));
|
||||
ATF_CHECK_EQ_MSG(qctx->zone, NULL,
|
||||
"test \"%s\" on line %d: "
|
||||
"no zone was expected to be attached to "
|
||||
"query context, but some was",
|
||||
test->id.description, test->id.lineno);
|
||||
ATF_CHECK_EQ_MSG(qctx->db, NULL,
|
||||
"test \"%s\" on line %d: "
|
||||
"no database was expected to be attached to "
|
||||
"query context, but some was",
|
||||
test->id.description, test->id.lineno);
|
||||
break;
|
||||
case NS__QUERY_START_R_CACHE:
|
||||
ATF_CHECK_EQ_MSG(qctx->result, ISC_R_SUCCESS,
|
||||
"test \"%s\" on line %d: "
|
||||
"expected success, got %s",
|
||||
test->id.description, test->id.lineno,
|
||||
isc_result_totext(qctx->result));
|
||||
ATF_CHECK_EQ_MSG(qctx->zone, NULL,
|
||||
"test \"%s\" on line %d: "
|
||||
"no zone was expected to be attached to "
|
||||
"query context, but some was",
|
||||
test->id.description, test->id.lineno);
|
||||
ATF_CHECK_MSG((qctx->db != NULL &&
|
||||
qctx->db == qctx->client->view->cachedb),
|
||||
"test \"%s\" on line %d: "
|
||||
"cache database was expected to be attached to "
|
||||
"query context, but it was not",
|
||||
test->id.description, test->id.lineno);
|
||||
break;
|
||||
case NS__QUERY_START_R_AUTH:
|
||||
ATF_CHECK_EQ_MSG(qctx->result, ISC_R_SUCCESS,
|
||||
"test \"%s\" on line %d: "
|
||||
"expected success, got %s",
|
||||
test->id.description, test->id.lineno,
|
||||
isc_result_totext(qctx->result));
|
||||
ATF_CHECK_MSG(qctx->zone != NULL,
|
||||
"test \"%s\" on line %d: "
|
||||
"a zone was expected to be attached to query "
|
||||
"context, but it was not",
|
||||
test->id.description, test->id.lineno);
|
||||
ATF_CHECK_MSG((qctx->db != NULL &&
|
||||
qctx->db != qctx->client->view->cachedb),
|
||||
"test \"%s\" on line %d: "
|
||||
"cache database was not expected to be attached "
|
||||
"to query context, but it is",
|
||||
test->id.description, test->id.lineno);
|
||||
break;
|
||||
case NS__QUERY_START_R_INVALID:
|
||||
ATF_REQUIRE_MSG(ISC_FALSE,
|
||||
"test \"%s\" on line %d has no expected "
|
||||
"result set",
|
||||
test->id.description, test->id.lineno);
|
||||
break;
|
||||
default:
|
||||
INSIST(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
if (test->auth_zone_path != NULL) {
|
||||
ns_test_cleanup_zone();
|
||||
}
|
||||
ns_test_qctx_destroy(&qctx);
|
||||
}
|
||||
|
||||
ATF_TC(ns__query_start);
|
||||
ATF_TC_HEAD(ns__query_start, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "ns__query_start()");
|
||||
}
|
||||
ATF_TC_BODY(ns__query_start, tc) {
|
||||
isc_result_t result;
|
||||
size_t i;
|
||||
|
||||
const ns__query_start_test_params_t tests[] = {
|
||||
/*
|
||||
* Recursive foo/A query to a server without recursive service
|
||||
* and no zones configured. Query should be REFUSED.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("foo/A, no cache, no auth"),
|
||||
.qname = "foo",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_FALSE,
|
||||
.expected_result = NS__QUERY_START_R_REFUSE,
|
||||
},
|
||||
/*
|
||||
* Recursive foo/A query to a server with recursive service and
|
||||
* no zones configured. Query should be answered from cache.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("foo/A, cache, no auth"),
|
||||
.qname = "foo",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.expected_result = NS__QUERY_START_R_CACHE,
|
||||
},
|
||||
/*
|
||||
* Recursive foo/A query to a server with recursive service and
|
||||
* zone "foo" configured. Query should be answered from
|
||||
* authoritative data.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("foo/A, RD=1, cache, auth for foo"),
|
||||
.qname = "foo",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_AUTH,
|
||||
},
|
||||
/*
|
||||
* Recursive bar/A query to a server without recursive service
|
||||
* and zone "foo" configured. Query should be REFUSED.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("bar/A, RD=1, no cache, auth for foo"),
|
||||
.qname = "bar",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_FALSE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_REFUSE,
|
||||
},
|
||||
/*
|
||||
* Recursive bar/A query to a server with recursive service and
|
||||
* zone "foo" configured. Query should be answered from
|
||||
* cache.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("bar/A, RD=1, cache, auth for foo"),
|
||||
.qname = "bar",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_CACHE,
|
||||
},
|
||||
/*
|
||||
* Recursive bar.foo/DS query to a server with recursive
|
||||
* service and zone "foo" configured. Query should be answered
|
||||
* from authoritative data.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("bar.foo/DS, RD=1, cache, auth for foo"),
|
||||
.qname = "bar.foo",
|
||||
.qtype = dns_rdatatype_ds,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_AUTH,
|
||||
},
|
||||
/*
|
||||
* Non-recursive bar.foo/DS query to a server with recursive
|
||||
* service and zone "foo" configured. Query should be answered
|
||||
* from authoritative data.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("bar.foo/DS, RD=0, cache, auth for foo"),
|
||||
.qname = "bar.foo",
|
||||
.qtype = dns_rdatatype_ds,
|
||||
.qflags = 0,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_AUTH,
|
||||
},
|
||||
/*
|
||||
* Recursive foo/DS query to a server with recursive service
|
||||
* and zone "foo" configured. Query should be answered from
|
||||
* cache.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("foo/DS, RD=1, cache, auth for foo"),
|
||||
.qname = "foo",
|
||||
.qtype = dns_rdatatype_ds,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_CACHE,
|
||||
},
|
||||
/*
|
||||
* Non-recursive foo/DS query to a server with recursive
|
||||
* service and zone "foo" configured. Query should be answered
|
||||
* from authoritative data.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("foo/DS, RD=0, cache, auth for foo"),
|
||||
.qname = "foo",
|
||||
.qtype = dns_rdatatype_ds,
|
||||
.qflags = 0,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.auth_zone_origin = "foo",
|
||||
.auth_zone_path = "testdata/query/foo.db",
|
||||
.expected_result = NS__QUERY_START_R_AUTH,
|
||||
},
|
||||
/*
|
||||
* Recursive _foo/A query to a server with recursive service,
|
||||
* no zones configured and owner name checks disabled. Query
|
||||
* should be answered from cache.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("_foo/A, cache, no auth, name checks off"),
|
||||
.qname = "_foo",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.disable_name_checks = ISC_TRUE,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.expected_result = NS__QUERY_START_R_CACHE,
|
||||
},
|
||||
/*
|
||||
* Recursive _foo/A query to a server with recursive service,
|
||||
* no zones configured and owner name checks enabled. Query
|
||||
* should be REFUSED.
|
||||
*/
|
||||
{
|
||||
NS_TEST_ID("_foo/A, cache, no auth, name checks on"),
|
||||
.qname = "_foo",
|
||||
.qtype = dns_rdatatype_a,
|
||||
.qflags = DNS_MESSAGEFLAG_RD,
|
||||
.disable_name_checks = ISC_FALSE,
|
||||
.recursive_service = ISC_TRUE,
|
||||
.expected_result = NS__QUERY_START_R_REFUSE,
|
||||
},
|
||||
};
|
||||
|
||||
UNUSED(tc);
|
||||
|
||||
result = ns_test_begin(NULL, ISC_TRUE);
|
||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
||||
|
||||
for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
|
||||
ns__query_start_test(&tests[i]);
|
||||
}
|
||||
|
||||
ns_test_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* Main
|
||||
*/
|
||||
ATF_TP_ADD_TCS(tp) {
|
||||
ATF_TP_ADD_TC(tp, ns__query_sfcache);
|
||||
ATF_TP_ADD_TC(tp, ns__query_start);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
15
lib/ns/tests/testdata/query/foo.db
vendored
Normal file
15
lib/ns/tests/testdata/query/foo.db
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
; Copyright (C) 2017 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/.
|
||||
|
||||
$TTL 3600
|
||||
@ IN SOA localhost. postmaster.localhost. (
|
||||
1 ;serial
|
||||
3600 ;refresh
|
||||
1800 ;retry
|
||||
604800 ;expiration
|
||||
3600 ) ;minimum
|
||||
IN NS ns
|
||||
ns IN A 127.0.0.1
|
Reference in New Issue
Block a user