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
|
4771. [bug] When sending RFC 5011 refresh queries, disregard
|
||||||
cached DNSKEY rrsets. [RT #46251]
|
cached DNSKEY rrsets. [RT #46251]
|
||||||
|
|
||||||
|
17
configure
vendored
17
configure
vendored
@@ -940,6 +940,7 @@ infodir
|
|||||||
docdir
|
docdir
|
||||||
oldincludedir
|
oldincludedir
|
||||||
includedir
|
includedir
|
||||||
|
runstatedir
|
||||||
localstatedir
|
localstatedir
|
||||||
sharedstatedir
|
sharedstatedir
|
||||||
sysconfdir
|
sysconfdir
|
||||||
@@ -1103,6 +1104,7 @@ datadir='${datarootdir}'
|
|||||||
sysconfdir='${prefix}/etc'
|
sysconfdir='${prefix}/etc'
|
||||||
sharedstatedir='${prefix}/com'
|
sharedstatedir='${prefix}/com'
|
||||||
localstatedir='${prefix}/var'
|
localstatedir='${prefix}/var'
|
||||||
|
runstatedir='${localstatedir}/run'
|
||||||
includedir='${prefix}/include'
|
includedir='${prefix}/include'
|
||||||
oldincludedir='/usr/include'
|
oldincludedir='/usr/include'
|
||||||
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
||||||
@@ -1355,6 +1357,15 @@ do
|
|||||||
| -silent | --silent | --silen | --sile | --sil)
|
| -silent | --silent | --silen | --sile | --sil)
|
||||||
silent=yes ;;
|
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)
|
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||||
ac_prev=sbindir ;;
|
ac_prev=sbindir ;;
|
||||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||||
@@ -1492,7 +1503,7 @@ fi
|
|||||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||||
libdir localedir mandir
|
libdir localedir mandir runstatedir
|
||||||
do
|
do
|
||||||
eval ac_val=\$$ac_var
|
eval ac_val=\$$ac_var
|
||||||
# Remove trailing slashes.
|
# Remove trailing slashes.
|
||||||
@@ -1645,6 +1656,7 @@ Fine tuning of the installation directories:
|
|||||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||||
|
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||||
--includedir=DIR C header files [PREFIX/include]
|
--includedir=DIR C header files [PREFIX/include]
|
||||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||||
@@ -11466,7 +11478,7 @@ fi
|
|||||||
XTARGETS=
|
XTARGETS=
|
||||||
case "$enable_developer" in
|
case "$enable_developer" in
|
||||||
yes)
|
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_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
||||||
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
||||||
test "${with_atf+set}" = set || with_atf=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
|
$as_echo "#define ATF_TEST 1" >>confdefs.h
|
||||||
|
|
||||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||||
|
STD_CDEFINES="$STD_CDEFINES -DNS_HOOKS_ENABLE=1"
|
||||||
ATFBIN="$atf/bin"
|
ATFBIN="$atf/bin"
|
||||||
ATFLIBS="-L$atf/lib -latf-c"
|
ATFLIBS="-L$atf/lib -latf-c"
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for exp in -lm" >&5
|
{ $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=
|
XTARGETS=
|
||||||
case "$enable_developer" in
|
case "$enable_developer" in
|
||||||
yes)
|
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_fixed_rrset+set}" = set || enable_fixed_rrset=yes
|
||||||
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
test "${enable_querytrace+set}" = set || enable_querytrace=yes
|
||||||
test "${with_atf+set}" = set || with_atf=yes
|
test "${with_atf+set}" = set || with_atf=yes
|
||||||
@@ -4771,6 +4771,7 @@ ATFLIBS=
|
|||||||
if test "no" != "$atf"; then
|
if test "no" != "$atf"; then
|
||||||
AC_DEFINE(ATF_TEST, 1, [define if ATF unit tests are to be built.])
|
AC_DEFINE(ATF_TEST, 1, [define if ATF unit tests are to be built.])
|
||||||
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
STD_CINCLUDES="$STD_CINCLUDES -I$atf/include"
|
||||||
|
STD_CDEFINES="$STD_CDEFINES -DNS_HOOKS_ENABLE=1"
|
||||||
ATFBIN="$atf/bin"
|
ATFBIN="$atf/bin"
|
||||||
ATFLIBS="-L$atf/lib -latf-c"
|
ATFLIBS="-L$atf/lib -latf-c"
|
||||||
AC_CHECK_LIB(m, exp, libm=yes, libm=no)
|
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 <isc/netaddr.h>
|
||||||
|
|
||||||
#include <dns/rdataset.h>
|
#include <dns/rdataset.h>
|
||||||
|
#include <dns/resolver.h>
|
||||||
#include <dns/rpz.h>
|
#include <dns/rpz.h>
|
||||||
#include <dns/types.h>
|
#include <dns/types.h>
|
||||||
|
|
||||||
@@ -92,6 +93,55 @@ struct ns_query {
|
|||||||
#define NS_QUERYATTR_RRL_CHECKED 0x10000
|
#define NS_QUERYATTR_RRL_CHECKED 0x10000
|
||||||
#define NS_QUERYATTR_REDIRECT 0x20000
|
#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
|
isc_result_t
|
||||||
ns_query_init(ns_client_t *client);
|
ns_query_init(ns_client_t *client);
|
||||||
|
|
||||||
@@ -104,4 +154,16 @@ ns_query_start(ns_client_t *client);
|
|||||||
void
|
void
|
||||||
ns_query_cancel(ns_client_t *client);
|
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 */
|
#endif /* NS_QUERY_H */
|
||||||
|
167
lib/ns/query.c
167
lib/ns/query.c
@@ -62,6 +62,8 @@
|
|||||||
#include <ns/stats.h>
|
#include <ns/stats.h>
|
||||||
#include <ns/xfrout.h>
|
#include <ns/xfrout.h>
|
||||||
|
|
||||||
|
#include "hooks.h"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/*
|
/*
|
||||||
* It has been recommended that DNS64 be changed to return excluded
|
* 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, ...)
|
log_noexistnodata(void *val, int level, const char *fmt, ...)
|
||||||
ISC_FORMAT_PRINTF(3, 4);
|
ISC_FORMAT_PRINTF(3, 4);
|
||||||
|
|
||||||
/*%
|
#ifdef NS_HOOKS_ENABLE
|
||||||
* The structure and functions defined below implement the query logic
|
|
||||||
* that previously lived in the single very complex function query_find().
|
LIBNS_EXTERNAL_DATA const ns_hook_t *ns__hook_table = NULL;
|
||||||
* The query_ctx_t structure maintains state from function to function.
|
|
||||||
* The call flow for the general query processing algorithm is described
|
#define PROCESS_HOOK(...) \
|
||||||
* below:
|
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
|
* 1. Set up query context and other resources for a client
|
||||||
* query (query_setup())
|
* 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;
|
* 3. Identify authoritative data sources which may have an answer;
|
||||||
* search them (query_lookup()). If an answer is found, go to 7.
|
* 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.)
|
* 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
|
static void
|
||||||
query_trace(query_ctx_t *qctx);
|
query_trace(query_ctx_t *qctx);
|
||||||
|
|
||||||
@@ -367,9 +337,6 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
|
|||||||
static isc_result_t
|
static isc_result_t
|
||||||
query_setup(ns_client_t *client, dns_rdatatype_t qtype);
|
query_setup(ns_client_t *client, dns_rdatatype_t qtype);
|
||||||
|
|
||||||
static isc_result_t
|
|
||||||
query_start(query_ctx_t *qctx);
|
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
query_lookup(query_ctx_t *qctx);
|
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
|
static isc_result_t
|
||||||
query_resume(query_ctx_t *qctx);
|
query_resume(query_ctx_t *qctx);
|
||||||
|
|
||||||
static isc_result_t
|
|
||||||
query_sfcache(query_ctx_t *qctx);
|
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
query_checkrrl(query_ctx_t *qctx, isc_result_t result);
|
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'.
|
* Set up query processing for the current query of 'client'.
|
||||||
* Calls qctx_init() to initialize a query context, checks
|
* 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
|
* This is called only from ns_query_start(), to begin a query
|
||||||
* for the first time. Restarting an existing query (for
|
* for the first time. Restarting an existing query (for
|
||||||
* instance, to handle CNAME lookups), is done by calling
|
* 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().
|
* recursion is handled by query_resume().
|
||||||
*/
|
*/
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
@@ -5137,14 +5101,6 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) {
|
|||||||
qctx_init(client, NULL, qtype, &qctx);
|
qctx_init(client, NULL, qtype, &qctx);
|
||||||
query_trace(&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.
|
* 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.qtype == dns_rdatatype_sig)
|
||||||
{
|
{
|
||||||
qctx.type = dns_rdatatype_any;
|
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
|
* follow a CNAME chain. Determines which authoritative database to
|
||||||
* search, then hands off processing to query_lookup().
|
* search, then hands off processing to query_lookup().
|
||||||
*/
|
*/
|
||||||
static isc_result_t
|
isc_result_t
|
||||||
query_start(query_ctx_t *qctx) {
|
ns__query_start(query_ctx_t *qctx) {
|
||||||
isc_result_t result;
|
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->want_restart = ISC_FALSE;
|
||||||
qctx->authoritative = ISC_FALSE;
|
qctx->authoritative = ISC_FALSE;
|
||||||
qctx->version = NULL;
|
qctx->version = NULL;
|
||||||
@@ -5206,6 +5170,12 @@ query_start(query_ctx_t *qctx) {
|
|||||||
if (dns_rdatatype_atparent(qctx->qtype) &&
|
if (dns_rdatatype_atparent(qctx->qtype) &&
|
||||||
!dns_name_equal(qctx->client->query.qname, dns_rootname))
|
!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;
|
qctx->options |= DNS_GETDB_NOEXACT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5218,8 +5188,10 @@ query_start(query_ctx_t *qctx) {
|
|||||||
(qctx->options & DNS_GETDB_NOEXACT) != 0))
|
(qctx->options & DNS_GETDB_NOEXACT) != 0))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If the query type is DS, look to see if we are
|
* This is a non-recursive QTYPE=DS query with QNAME whose
|
||||||
* authoritative for the child zone.
|
* 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_db_t *tdb = NULL;
|
||||||
dns_zone_t *tzone = NULL;
|
dns_zone_t *tzone = NULL;
|
||||||
@@ -5232,6 +5204,10 @@ query_start(query_ctx_t *qctx) {
|
|||||||
DNS_GETDB_PARTIAL,
|
DNS_GETDB_PARTIAL,
|
||||||
&tzone, &tdb, &tversion);
|
&tzone, &tdb, &tversion);
|
||||||
if (tresult == ISC_R_SUCCESS) {
|
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;
|
qctx->options &= ~DNS_GETDB_NOEXACT;
|
||||||
query_putrdataset(qctx->client, &qctx->rdataset);
|
query_putrdataset(qctx->client, &qctx->rdataset);
|
||||||
if (qctx->db != NULL) {
|
if (qctx->db != NULL) {
|
||||||
@@ -5247,6 +5223,10 @@ query_start(query_ctx_t *qctx) {
|
|||||||
qctx->is_zone = ISC_TRUE;
|
qctx->is_zone = ISC_TRUE;
|
||||||
result = ISC_R_SUCCESS;
|
result = ISC_R_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* We are not authoritative for QNAME. Clean up and
|
||||||
|
* leave result as it was.
|
||||||
|
*/
|
||||||
if (tdb != NULL) {
|
if (tdb != NULL) {
|
||||||
dns_db_detach(&tdb);
|
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 != ISC_R_SUCCESS) {
|
||||||
if (result == DNS_R_REFUSED) {
|
if (result == DNS_R_REFUSED) {
|
||||||
if (WANTRECURSION(qctx->client)) {
|
if (WANTRECURSION(qctx->client)) {
|
||||||
@@ -5269,12 +5254,17 @@ query_start(query_ctx_t *qctx) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CCTRACE(ISC_LOG_ERROR,
|
CCTRACE(ISC_LOG_ERROR,
|
||||||
"query_start: query_getdb failed");
|
"ns__query_start: query_getdb failed");
|
||||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||||
}
|
}
|
||||||
return (query_done(qctx));
|
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;
|
qctx->is_staticstub_zone = ISC_FALSE;
|
||||||
if (qctx->is_zone) {
|
if (qctx->is_zone) {
|
||||||
qctx->authoritative = ISC_TRUE;
|
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->event == NULL && qctx->client->query.restarts == 0) {
|
||||||
if (qctx->is_zone) {
|
if (qctx->is_zone) {
|
||||||
if (qctx->zone != NULL) {
|
if (qctx->zone != NULL) {
|
||||||
@@ -5326,6 +5320,8 @@ query_lookup(query_ctx_t *qctx) {
|
|||||||
|
|
||||||
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
|
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
|
||||||
|
|
||||||
|
PROCESS_HOOK(NS_QUERY_LOOKUP_BEGIN, qctx);
|
||||||
|
|
||||||
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
dns_clientinfomethods_init(&cm, ns_client_sourceip);
|
||||||
dns_clientinfo_init(&ci, qctx->client, NULL);
|
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
|
* 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
|
* 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
|
* 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
|
isc_result_t
|
||||||
query_sfcache(query_ctx_t *qctx) {
|
ns__query_sfcache(query_ctx_t *qctx) {
|
||||||
isc_boolean_t failcache;
|
isc_boolean_t failcache;
|
||||||
isc_uint32_t flags;
|
isc_uint32_t flags;
|
||||||
|
|
||||||
@@ -10317,7 +10314,7 @@ query_glueanswer(query_ctx_t *qctx) {
|
|||||||
*
|
*
|
||||||
* - Clean up
|
* - Clean up
|
||||||
* - If we have an answer ready (positive or negative), send it.
|
* - 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
|
* - If we've started recursion, then just clean up; things will be
|
||||||
* restarted via fetch_callback()/query_resume().
|
* 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;
|
const dns_namelist_t *secs = qctx->client->message->sections;
|
||||||
CCTRACE(ISC_LOG_DEBUG(3), "query_done");
|
CCTRACE(ISC_LOG_DEBUG(3), "query_done");
|
||||||
|
|
||||||
|
PROCESS_HOOK(NS_QUERY_DONE_BEGIN, qctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* General cleanup.
|
* General cleanup.
|
||||||
*/
|
*/
|
||||||
@@ -10352,7 +10351,7 @@ query_done(query_ctx_t *qctx) {
|
|||||||
*/
|
*/
|
||||||
if (qctx->want_restart && qctx->client->query.restarts < MAX_RESTARTS) {
|
if (qctx->want_restart && qctx->client->query.restarts < MAX_RESTARTS) {
|
||||||
qctx->client->query.restarts++;
|
qctx->client->query.restarts++;
|
||||||
return (query_start(qctx));
|
return (ns__query_start(qctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qctx->want_stale) {
|
if (qctx->want_stale) {
|
||||||
|
@@ -32,11 +32,13 @@ LIBS = @LIBS@ @ATFLIBS@
|
|||||||
OBJS = nstest.@O@
|
OBJS = nstest.@O@
|
||||||
SRCS = nstest.c \
|
SRCS = nstest.c \
|
||||||
listenlist_test.c \
|
listenlist_test.c \
|
||||||
notify_test.c
|
notify_test.c \
|
||||||
|
query_test.c
|
||||||
|
|
||||||
SUBDIRS =
|
SUBDIRS =
|
||||||
TARGETS = listenlist_test@EXEEXT@ \
|
TARGETS = listenlist_test@EXEEXT@ \
|
||||||
notify_test@EXEEXT@
|
notify_test@EXEEXT@ \
|
||||||
|
query_test
|
||||||
|
|
||||||
@BIND9_MAKE_RULES@
|
@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} \
|
notify_test.@O@ nstest.@O@ ${NSLIBS} ${DNSLIBS} \
|
||||||
${ISCLIBS} ${LIBS}
|
${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::
|
unit::
|
||||||
sh ${top_srcdir}/unit/unittest.sh
|
sh ${top_srcdir}/unit/unittest.sh
|
||||||
|
|
||||||
|
@@ -28,49 +28,6 @@
|
|||||||
|
|
||||||
#include "nstest.h"
|
#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
|
static void
|
||||||
check_response(isc_buffer_t *buf) {
|
check_response(isc_buffer_t *buf) {
|
||||||
isc_result_t result;
|
isc_result_t result;
|
||||||
@@ -113,7 +70,12 @@ ATF_TC_BODY(notify_start, tc) {
|
|||||||
result = ns_test_getclient(NULL, ISC_FALSE, &client);
|
result = ns_test_getclient(NULL, ISC_FALSE, &client);
|
||||||
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
|
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.
|
* 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
|
* Set up client object with this message and test the NOTIFY
|
||||||
* handler.
|
* handler.
|
||||||
*/
|
*/
|
||||||
dns_view_attach(view, &client->view);
|
|
||||||
if (client->message != NULL) {
|
if (client->message != NULL) {
|
||||||
dns_message_destroy(&client->message);
|
dns_message_destroy(&client->message);
|
||||||
}
|
}
|
||||||
@@ -148,7 +109,7 @@ ATF_TC_BODY(notify_start, tc) {
|
|||||||
/*
|
/*
|
||||||
* Clean up
|
* Clean up
|
||||||
*/
|
*/
|
||||||
cleanup_zone();
|
ns_test_cleanup_zone();
|
||||||
|
|
||||||
ns_client_detach(&client);
|
ns_client_detach(&client);
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include <isc/mem.h>
|
#include <isc/mem.h>
|
||||||
#include <isc/os.h>
|
#include <isc/os.h>
|
||||||
#include <isc/print.h>
|
#include <isc/print.h>
|
||||||
|
#include <isc/random.h>
|
||||||
#include <isc/string.h>
|
#include <isc/string.h>
|
||||||
#include <isc/socket.h>
|
#include <isc/socket.h>
|
||||||
#include <isc/stdio.h>
|
#include <isc/stdio.h>
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
#include <isc/timer.h>
|
#include <isc/timer.h>
|
||||||
#include <isc/util.h>
|
#include <isc/util.h>
|
||||||
|
|
||||||
|
#include <dns/cache.h>
|
||||||
#include <dns/db.h>
|
#include <dns/db.h>
|
||||||
#include <dns/dispatch.h>
|
#include <dns/dispatch.h>
|
||||||
#include <dns/fixedname.h>
|
#include <dns/fixedname.h>
|
||||||
@@ -42,6 +44,8 @@
|
|||||||
#include <ns/interfacemgr.h>
|
#include <ns/interfacemgr.h>
|
||||||
#include <ns/server.h>
|
#include <ns/server.h>
|
||||||
|
|
||||||
|
#include "../hooks.h"
|
||||||
|
|
||||||
#include "nstest.h"
|
#include "nstest.h"
|
||||||
|
|
||||||
isc_mem_t *mctx = NULL;
|
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 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.
|
* 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)
|
if (chdir(TESTS) == -1)
|
||||||
CHECK(ISC_R_FAILURE);
|
CHECK(ISC_R_FAILURE);
|
||||||
|
|
||||||
|
ns__hook_table = NULL;
|
||||||
|
|
||||||
return (ISC_R_SUCCESS);
|
return (ISC_R_SUCCESS);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@@ -306,15 +314,29 @@ ns_test_end(void) {
|
|||||||
isc_mem_destroy(&mctx);
|
isc_mem_destroy(&mctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a view.
|
|
||||||
*/
|
|
||||||
isc_result_t
|
isc_result_t
|
||||||
ns_test_makeview(const char *name, dns_view_t **viewp) {
|
ns_test_makeview(const char *name, isc_boolean_t with_cache,
|
||||||
isc_result_t result;
|
dns_view_t **viewp)
|
||||||
|
{
|
||||||
|
dns_cache_t *cache = NULL;
|
||||||
dns_view_t *view = NULL;
|
dns_view_t *view = NULL;
|
||||||
|
isc_result_t result;
|
||||||
|
|
||||||
CHECK(dns_view_create(mctx, dns_rdataclass_in, name, &view));
|
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;
|
*viewp = view;
|
||||||
|
|
||||||
return (ISC_R_SUCCESS);
|
return (ISC_R_SUCCESS);
|
||||||
@@ -417,6 +439,79 @@ ns_test_closezonemgr(void) {
|
|||||||
dns_zonemgr_detach(&zonemgr);
|
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
|
isc_result_t
|
||||||
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
||||||
ns_client_t **clientp)
|
ns_client_t **clientp)
|
||||||
@@ -435,6 +530,291 @@ ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
|||||||
return (result);
|
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.
|
* Sleep for 'usec' microseconds.
|
||||||
*/
|
*/
|
||||||
|
@@ -26,6 +26,13 @@
|
|||||||
#include <ns/interfacemgr.h>
|
#include <ns/interfacemgr.h>
|
||||||
#include <ns/client.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) \
|
#define CHECK(r) \
|
||||||
do { \
|
do { \
|
||||||
result = (r); \
|
result = (r); \
|
||||||
@@ -55,8 +62,13 @@ ns_test_begin(FILE *logfile, isc_boolean_t create_managers);
|
|||||||
void
|
void
|
||||||
ns_test_end(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
|
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
|
isc_result_t
|
||||||
ns_test_makezone(const char *name, dns_zone_t **zonep, dns_view_t *view,
|
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
|
void
|
||||||
ns_test_closezonemgr(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
|
void
|
||||||
ns_test_nap(isc_uint32_t usec);
|
ns_test_nap(isc_uint32_t usec);
|
||||||
|
|
||||||
@@ -88,3 +115,37 @@ ns_test_getdata(const char *file, unsigned char *buf,
|
|||||||
isc_result_t
|
isc_result_t
|
||||||
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
ns_test_getclient(ns_interface_t *ifp0, isc_boolean_t tcp,
|
||||||
ns_client_t **clientp);
|
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