diff --git a/daemon/cachedump.c b/daemon/cachedump.c index baf8008ea..943eb63f3 100644 --- a/daemon/cachedump.c +++ b/daemon/cachedump.c @@ -387,7 +387,7 @@ move_into_cache(struct ub_packed_rrset_key* k, struct rrset_ref ref; uint8_t* p; - ak = alloc_special_obtain(&worker->alloc); + ak = alloc_special_obtain(worker->alloc); if(!ak) { log_warn("error out of memory"); return 0; @@ -398,7 +398,7 @@ move_into_cache(struct ub_packed_rrset_key* k, ak->rk.dname = (uint8_t*)memdup(k->rk.dname, k->rk.dname_len); if(!ak->rk.dname) { log_warn("error out of memory"); - ub_packed_rrset_parsedelete(ak, &worker->alloc); + ub_packed_rrset_parsedelete(ak, worker->alloc); return 0; } s = sizeof(*ad) + (sizeof(size_t) + sizeof(uint8_t*) + @@ -408,7 +408,7 @@ move_into_cache(struct ub_packed_rrset_key* k, ad = (struct packed_rrset_data*)malloc(s); if(!ad) { log_warn("error out of memory"); - ub_packed_rrset_parsedelete(ak, &worker->alloc); + ub_packed_rrset_parsedelete(ak, worker->alloc); return 0; } p = (uint8_t*)ad; @@ -431,7 +431,8 @@ move_into_cache(struct ub_packed_rrset_key* k, ref.key = ak; ref.id = ak->id; (void)rrset_cache_update(worker->env.rrset_cache, &ref, - &worker->alloc, *worker->env.now); + worker->alloc, *worker->env.now); + return 1; } diff --git a/daemon/daemon.c b/daemon/daemon.c index 71091133a..193608d40 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -488,6 +488,27 @@ static int daemon_get_shufport(struct daemon* daemon, int* shufport) return avail; } +/** + * Clear and delete per-worker alloc caches, and free memory maintained in + * superalloc. + * The rrset and message caches must be empty at the time of call. + * @param daemon: the daemon that maintains the alloc caches to be cleared. + */ +static void +daemon_clear_allocs(struct daemon* daemon) +{ + int i; + + for(i=0; inum; i++) { + alloc_clear(daemon->worker_allocs[i]); + free(daemon->worker_allocs[i]); + } + free(daemon->worker_allocs); + daemon->worker_allocs = NULL; + + alloc_clear_special(&daemon->superalloc); +} + /** * Allocate empty worker structures. With backptr and thread-number, * from 0..numthread initialised. Used as user arguments to new threads. @@ -540,6 +561,21 @@ daemon_create_workers(struct daemon* daemon) /* the above is not ports/numthr, due to rounding */ fatal_exit("could not create worker"); } + /* create per-worker alloc caches if not reusing existing ones. */ + if(!daemon->worker_allocs) { + daemon->worker_allocs = (struct alloc_cache**)calloc( + (size_t)daemon->num, sizeof(struct alloc_cache*)); + if(!daemon->worker_allocs) + fatal_exit("could not allocate worker allocs"); + for(i=0; inum; i++) { + struct alloc_cache* alloc = calloc(1, + sizeof(struct alloc_cache)); + if (!alloc) + fatal_exit("could not allocate worker alloc"); + alloc_init(alloc, &daemon->superalloc, i); + daemon->worker_allocs[i] = alloc; + } + } free(shufport); } @@ -771,6 +807,7 @@ daemon_fork(struct daemon* daemon) /* Shutdown SHM */ shm_main_shutdown(daemon); + daemon->reuse_cache = daemon->workers[0]->reuse_cache; daemon->need_to_exit = daemon->workers[0]->need_to_exit; } @@ -785,9 +822,16 @@ daemon_cleanup(struct daemon* daemon) log_thread_set(NULL); /* clean up caches because * a) RRset IDs will be recycled after a reload, causing collisions - * b) validation config can change, thus rrset, msg, keycache clear */ - slabhash_clear(&daemon->env->rrset_cache->table); - slabhash_clear(daemon->env->msg_cache); + * b) validation config can change, thus rrset, msg, keycache clear + * + * If we are trying to keep the cache as long as possible, we should + * defer the cleanup until we know whether the new configuration allows + * the reuse. (If we're exiting, cleanup should be done here). */ + if(!daemon->reuse_cache || daemon->need_to_exit) { + slabhash_clear(&daemon->env->rrset_cache->table); + slabhash_clear(daemon->env->msg_cache); + } + daemon->old_num = daemon->num; /* save the current num */ local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; respip_set_delete(daemon->respip_set); @@ -802,8 +846,13 @@ daemon_cleanup(struct daemon* daemon) worker_delete(daemon->workers[i]); free(daemon->workers); daemon->workers = NULL; + /* Unless we're trying to keep the cache, worker alloc_caches should be + * cleared and freed here. We do this after deleting workers to + * guarantee that the alloc caches are valid throughout the lifetime + * of workers. */ + if(!daemon->reuse_cache || daemon->need_to_exit) + daemon_clear_allocs(daemon); daemon->num = 0; - alloc_clear_special(&daemon->superalloc); #ifdef USE_DNSTAP dt_delete(daemon->dtenv); daemon->dtenv = NULL; @@ -900,8 +949,42 @@ daemon_delete(struct daemon* daemon) void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg) { + int new_num = cfg->num_threads?cfg->num_threads:1; + daemon->cfg = cfg; config_apply(cfg); + + /* If this is a reload and we deferred the decision on whether to + * reuse the alloc, RRset, and message caches, then check to see if + * it's safe to keep the caches: + * - changing the number of threads is obviously incompatible with + * keeping the per-thread alloc caches. It also means we have to + * clear RRset and message caches. (note that 'new_num' may be + * adjusted in daemon_create_workers, but for our purpose we can + * simply compare it with 'old_num'; if they are equal here, + * 'new_num' won't be adjusted to a different value than 'old_num'). + * - changing RRset cache size effectively clears any remaining cache + * entries. We could keep their keys in alloc caches, but it would + * be more consistent with the sense of the change to clear allocs + * and free memory. To do so we also have to clear message cache. + * - only changing message cache size does not necessarily affect + * RRset or alloc cache. But almost all new subsequent queries will + * require recursive resolution anyway, so it doesn't help much to + * just keep RRset and alloc caches. For simplicity we clear/free + * the other two, too. */ + if(daemon->worker_allocs && + (new_num != daemon->old_num || + !slabhash_is_size(daemon->env->msg_cache, cfg->msg_cache_size, + cfg->msg_cache_slabs) || + !slabhash_is_size(&daemon->env->rrset_cache->table, + cfg->rrset_cache_size, cfg->rrset_cache_slabs))) + { + log_warn("cannot reuse caches due to critical config change"); + slabhash_clear(&daemon->env->rrset_cache->table); + slabhash_clear(daemon->env->msg_cache); + daemon_clear_allocs(daemon); + } + if(!slabhash_is_size(daemon->env->msg_cache, cfg->msg_cache_size, cfg->msg_cache_slabs)) { slabhash_delete(daemon->env->msg_cache); diff --git a/daemon/daemon.h b/daemon/daemon.h index 58713e9ce..57665446d 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -99,8 +99,12 @@ struct daemon { void* listen_sslctx, *connect_sslctx; /** num threads allocated */ int num; + /** num threads allocated in the previous config or 0 at first */ + int old_num; /** the worker entries */ struct worker** workers; + /** per-worker allocation cache */ + struct alloc_cache **worker_allocs; /** do we need to exit unbound (or is it only a reload?) */ int need_to_exit; /** master random table ; used for port div between threads on reload*/ @@ -140,6 +144,8 @@ struct daemon { /** the dnscrypt environment */ struct dnsc_env* dnscenv; #endif + /** reuse existing cache on reload if other conditions allow it. */ + int reuse_cache; }; /** diff --git a/daemon/remote.c b/daemon/remote.c index 2b16021ce..7c5a036f3 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -682,8 +682,9 @@ do_stop(RES* ssl, struct worker* worker) /** do the reload command */ static void -do_reload(RES* ssl, struct worker* worker) +do_reload(RES* ssl, struct worker* worker, int reuse_cache) { + worker->reuse_cache = reuse_cache; worker->need_to_exit = 0; comm_base_exit(worker->base); send_ok(ssl); @@ -3029,8 +3030,11 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd, if(cmdcmp(p, "stop", 4)) { do_stop(ssl, worker); return; + } else if(cmdcmp(p, "reload_keep_cache", 17)) { + do_reload(ssl, worker, 1); + return; } else if(cmdcmp(p, "reload", 6)) { - do_reload(ssl, worker); + do_reload(ssl, worker, 0); return; } else if(cmdcmp(p, "stats_noreset", 13)) { do_stats(ssl, worker, 0); diff --git a/daemon/worker.c b/daemon/worker.c index 2180b4f96..99dcf9940 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -133,7 +133,7 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker), rrset = slabhash_get_mem(&worker->env.rrset_cache->table); infra = infra_get_mem(worker->env.infra_cache); mesh = mesh_get_mem(worker->env.mesh); - ac = alloc_get_mem(&worker->alloc); + ac = alloc_get_mem(worker->alloc); superac = alloc_get_mem(&worker->daemon->superalloc); anch = anchors_get_mem(worker->env.anchors); iter = 0; @@ -2065,15 +2065,14 @@ worker_init(struct worker* worker, struct config_file *cfg, } server_stats_init(&worker->stats, cfg); - alloc_init(&worker->alloc, &worker->daemon->superalloc, - worker->thread_num); - alloc_set_id_cleanup(&worker->alloc, &worker_alloc_cleanup, worker); + worker->alloc = worker->daemon->worker_allocs[worker->thread_num]; + alloc_set_id_cleanup(worker->alloc, &worker_alloc_cleanup, worker); worker->env = *worker->daemon->env; comm_base_timept(worker->base, &worker->env.now, &worker->env.now_tv); worker->env.worker = worker; worker->env.worker_base = worker->base; worker->env.send_query = &worker_send_query; - worker->env.alloc = &worker->alloc; + worker->env.alloc = worker->alloc; worker->env.outnet = worker->back; worker->env.rnd = worker->rndstate; /* If case prefetch is triggered, the corresponding mesh will clear @@ -2217,7 +2216,7 @@ worker_delete(struct worker* worker) #endif /* USE_DNSTAP */ comm_base_delete(worker->base); ub_randfree(worker->rndstate); - alloc_clear(&worker->alloc); + /* don't touch worker->alloc, as it's maintained in daemon */ regional_destroy(worker->env.scratch); regional_destroy(worker->scratchpad); free(worker); diff --git a/daemon/worker.h b/daemon/worker.h index 3fb52abd9..ab2fc728d 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -118,7 +118,7 @@ struct worker { /** do we need to restart or quit (on signal) */ int need_to_exit; /** allocation cache for this thread */ - struct alloc_cache alloc; + struct alloc_cache *alloc; /** per thread statistics */ struct ub_server_stats stats; /** thread scratch regional */ @@ -131,6 +131,8 @@ struct worker { /** dnstap environment, changed for this thread */ struct dt_env dtenv; #endif + /** reuse existing cache on reload if other conditions allow it. */ + int reuse_cache; }; /** diff --git a/doc/Changelog b/doc/Changelog index d51a45662..b14cf1d55 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +14 December 2022: George + - Merge #569 from JINMEI Tatuya: add keep-cache option to + 'unbound-control reload' to keep caches. + 13 December 2022: George - Expose 'statistics-inhibit-zero' as a configuration option; the default value retains Unbound's behavior. diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in index 39adb7643..fd165cb52 100644 --- a/doc/unbound-control.8.in +++ b/doc/unbound-control.8.in @@ -54,6 +54,12 @@ Stop the server. The server daemon exits. .B reload Reload the server. This flushes the cache and reads the config file fresh. .TP +.B reload_keep_cache +Reload the server but try to keep the RRset and message cache if +(re)configuration allows for it. +That means the caches sizes and the number of threads must not change between +reloads. +.TP .B verbosity \fInumber Change verbosity value for logging. Same values as \fBverbosity\fR keyword in \fIunbound.conf\fR(5). This new setting lasts until the server is issued diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c index 9fbf09736..821c490c3 100644 --- a/smallapp/unbound-control.c +++ b/smallapp/unbound-control.c @@ -102,6 +102,12 @@ usage(void) printf(" stop stops the server\n"); printf(" reload reloads the server\n"); printf(" (this flushes data, stats, requestlist)\n"); + printf(" reload_keep_cache reloads the server but tries to\n"); + printf(" keep the RRset and message cache\n"); + printf(" if (re)configuration allows for it.\n"); + printf(" That means the caches sizes and\n"); + printf(" the number of threads must not\n"); + printf(" change between reloads.\n"); printf(" stats print statistics\n"); printf(" stats_noreset peek at statistics\n"); #ifdef HAVE_SHMGET diff --git a/testdata/09-unbound-control.tdir/09-unbound-control.conf b/testdata/09-unbound-control.tdir/09-unbound-control.conf index ba55e34e8..227d56075 100644 --- a/testdata/09-unbound-control.tdir/09-unbound-control.conf +++ b/testdata/09-unbound-control.tdir/09-unbound-control.conf @@ -1,6 +1,6 @@ server: verbosity: 2 - # num-threads: 1 + num-threads: 1 interface: 127.0.0.1 port: @PORT@ use-syslog: no @@ -9,6 +9,10 @@ server: chroot: "" username: "" do-not-query-localhost: no + access-control: 127.0.0.1 allow_snoop + msg-cache-size: 4m + rrset-cache-size: 4m + minimal-responses: yes remote-control: control-enable: yes control-interface: 127.0.0.1 @@ -21,4 +25,3 @@ remote-control: forward-zone: name: "." forward-addr: "127.0.0.1@@TOPORT@" - diff --git a/testdata/09-unbound-control.tdir/09-unbound-control.test b/testdata/09-unbound-control.tdir/09-unbound-control.test index f683bf417..0ef679b3f 100644 --- a/testdata/09-unbound-control.tdir/09-unbound-control.test +++ b/testdata/09-unbound-control.tdir/09-unbound-control.test @@ -5,364 +5,317 @@ [ -f .tpkg.var.test ] && source .tpkg.var.test PRE="../.." +. ../common.sh -# exit value is 1 on usage -$PRE/unbound-control -h -if test $? -ne 1; then - echo "wrong exit value for usage." - exit 1 -else - echo "exit value for usage: OK" -fi +# End the test +# $1: exit value +end () { + echo "> cat logfiles" + cat fwd.log + cat unbound.log + exit $1 +} + +# Expect a given exit value of the previous command +# $1: the expected exit value +# $2: optional text to print when failing +expect_exit_value () { + if test $? -ne $1; then + if test -z "$2"; then + if test $1 -eq 1; then + msg="on error" + else + msg="after success" + fi + else + msg="$2" + fi + echo "wrong exit value $msg" + end 1 + fi +} + +# Helper function for quering +# $@: at least the domain name to query and optional dig arguments +query () { + echo "> dig $@" + dig @127.0.0.1 -p $UNBOUND_PORT $@ | tee outfile +} + +# Expect something in the answer +# $1: expected regular expression +expect_answer () { + echo "> check answer for \"$1\"" + if grep "$1" outfile; then + echo "OK" + else + echo "Not OK" + end 1 + fi +} + +# Fail the test for unexpected answers +# $1: unexpected regular expression +fail_answer () { + echo "> \"$1\" should not be in answer" + if grep "$1" outfile; then + echo "Not OK" + end 1 + else + echo "OK" + fi +} + +# Issue an unbound-control command +# $@: command arguments +control_command () { + echo "$PRE/unbound-control $@" + $PRE/unbound-control $@ > outfile +} + +# Dump the cache contents +# $@: optional options to unbound-control +cache_dump () { + echo "$PRE/unbound-control $@ dump_cache > cache.dump" + $PRE/unbound-control $@ dump_cache > cache.dump +} + +# Load cache contents +# $@: optional options to unbound-control +cache_load () { + echo "$PRE/unbound-control $@ load_cache < cache.dump" + $PRE/unbound-control $@ load_cache < cache.dump +} + +# Expect an entry in the cache dump +# $1: expected regular expression +expect_in_cache_dump () { + echo "> check cache dump for \"$1\"" + if grep "$1" cache.dump; then + echo "OK cache dump" + else + echo "Not OK cache dump" + end 1 + fi +} + +# Fail the test for unexpected entry in the cache dump +# $1: unexpected regular expression +fail_in_cache_dump () { + echo "> \"$1\" should not be in cache dump" + if grep "$1" cache.dump; then + echo "Not OK cache dump" + end 1 + else + echo "OK cache dump" + fi +} + +# start the test +cp ub.conf main.conf + +teststep "exit value is 1 on usage" +control_command -h +expect_exit_value 1 "for usage" # use lock-verify if possible -# test if the server is up. -echo "> dig www.example.com." -dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile -echo "> check answer" -if grep "10.20.30.40" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "test if the server is up" +query www.example.com. +expect_answer "10.20.30.40" -# exit value is 1 when a bad command is given. -echo "$PRE/unbound-control -c ub.conf blablargh" -$PRE/unbound-control -c ub.conf blablargh -if test $? -ne 1; then - echo "wrong exit value on error." - echo "> cat logfiles" - cat fwd.log - cat unbound.lo - exit 1 -else - echo "correct exit value on error" -fi +teststep "exit value is 1 when a bad command is given" +control_command -c ub.conf blablargh +expect_exit_value 1 # reload the server. test if the server came up by putting a new # local-data element in the server. +teststep "reload the server" echo "server: local-data: 'afterreload. IN A 5.6.7.8'" >> ub.conf -echo "$PRE/unbound-control -c ub.conf reload" -$PRE/unbound-control -c ub.conf reload -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +control_command -c ub.conf reload +expect_exit_value 0 +query afterreload. +expect_answer "5.6.7.8" -echo "> dig afterreload." -dig @127.0.0.1 -p $UNBOUND_PORT afterreload. | tee outfile -echo "> check answer" -if grep "5.6.7.8" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "must have had at least 1 query since reload" +control_command -c ub.conf stats +expect_exit_value 0 +expect_answer "^total.num.queries=[1-9][0-9]*$" -# must have had queries now. 1 since reload. -echo "$PRE/unbound-control -c ub.conf stats" -$PRE/unbound-control -c ub.conf stats > tmp.$$ -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -if grep "^total.num.queries=[1-9][0-9]*$" tmp.$$; then - echo "OK" -else - echo "bad stats" - cat tmp.$$ - exit 1 -fi +teststep "check verbosity" +control_command -c ub.conf verbosity 2 +expect_exit_value 0 -# verbosity -echo "$PRE/unbound-control -c ub.conf verbosity 2" -$PRE/unbound-control -c ub.conf verbosity 2 -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +teststep "check syntax error in parse" +control_command -c ub.conf verbosity jkdf +expect_exit_value 1 -# check syntax error in parse -echo "$PRE/unbound-control -c ub.conf verbosity jkdf" -$PRE/unbound-control -c ub.conf verbosity jkdf -if test $? -ne 1; then - echo "wrong exit value after failure" - exit 1 -fi - -# check bad credentials +teststep "check bad credentials" cp ub.conf bad.conf -echo "remote-control:" >> bad.conf -echo " server-key-file: bad_server.key" >> bad.conf -echo " server-cert-file: bad_server.pem" >> bad.conf -echo " control-key-file: bad_control.key" >> bad.conf -echo " control-cert-file: bad_control.pem" >> bad.conf -echo "$PRE/unbound-control -c bad.conf verbosity 2" -$PRE/unbound-control -c bad.conf verbosity 2 -if test $? -ne 1; then - echo "wrong exit value after failure" - exit 1 -fi +cat conf.bad_credentials >> bad.conf +control_command -c bad.conf verbosity 2 +expect_exit_value 1 -# check spoofedclient credentials +teststep "check spoofed client credentials" rm -f bad.conf cp ub.conf bad.conf -echo "remote-control:" >> bad.conf -echo " server-key-file: unbound_server.key" >> bad.conf -echo " server-cert-file: unbound_server.pem" >> bad.conf -echo " control-key-file: bad_control.key" >> bad.conf -echo " control-cert-file: bad_control.pem" >> bad.conf -echo "$PRE/unbound-control -c bad.conf verbosity 2" -$PRE/unbound-control -c bad.conf verbosity 2 -if test $? -ne 1; then - echo "wrong exit value after failure" - exit 1 -fi +cat conf.spoofed_credentials >> bad.conf +control_command -c bad.conf verbosity 2 +expect_exit_value 1 -# create a new local zone -echo "> test of local zone" -echo "$PRE/unbound-control -c ub.conf local_zone example.net static" -$PRE/unbound-control -c ub.conf local_zone example.net static -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "$PRE/unbound-control -c ub.conf local_data www.example.net A 192.0.2.1" -$PRE/unbound-control -c ub.conf local_data www.example.net A 192.0.2.1 -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +teststep "create a new local zone" +control_command -c ub.conf local_zone example.net static +expect_exit_value 0 +control_command -c ub.conf local_data www.example.net A 192.0.2.1 +expect_exit_value 0 -# check that www.example.net exists -echo "> dig www.example.net." -dig @127.0.0.1 -p $UNBOUND_PORT www.example.net. | tee outfile -echo "> check answer" -if grep "192.0.2.1" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "check that www.example.net exists" +query www.example.net. +expect_answer "192.0.2.1" -# check that mail.example.net has nxdomain -echo "> dig mail.example.net." -dig @127.0.0.1 -p $UNBOUND_PORT mail.example.net. | tee outfile -echo "> check answer" -if grep "NXDOMAIN" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "check that mail.example.net has nxdomain" +query mail.example.net. +expect_answer "NXDOMAIN" -# remove www.example.net - check it gets nxdomain -echo "$PRE/unbound-control -c ub.conf local_data_remove www.example.net" -$PRE/unbound-control -c ub.conf local_data_remove www.example.net -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> dig www.example.net." -dig @127.0.0.1 -p $UNBOUND_PORT www.example.net. | tee outfile -echo "> check answer" -if grep "NXDOMAIN" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "remove www.example.net - check it gets nxdomain" +control_command -c ub.conf local_data_remove www.example.net +expect_exit_value 0 +query www.example.net. +expect_answer "NXDOMAIN" -# remove nonexistent name - check bug#287(segfault) does not happen. -echo "$PRE/unbound-control -c ub.conf local_data_remove test.example.net" -$PRE/unbound-control -c ub.conf local_data_remove test.example.net +teststep "remove nonexistent name - check bug#287(segfault) does not happen" +control_command -c ub.conf local_data_remove test.example.net # if crash then then we get: error: could not SSL_read from unbound-control -if test $? -ne 0; then - echo "wrong exit value after success" - cat unbound.log - echo "Not OK" - exit 1 -fi +expect_exit_value 0 -# remove example.net - check its gone. -echo "$PRE/unbound-control -c ub.conf local_zone_remove example.net" -$PRE/unbound-control -c ub.conf local_zone_remove example.net -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> dig www.example.net." -dig @127.0.0.1 -p $UNBOUND_PORT www.example.net. | tee outfile -echo "> check answer" -if grep "SERVFAIL" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "remove example.net - check its gone" +control_command -c ub.conf local_zone_remove example.net +expect_exit_value 0 +query www.example.net. +expect_answer "SERVFAIL" -# dump the cache -echo "> test cache dump" -# fillup cache -echo "dig www.example.com" -dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. -echo "$PRE/unbound-control -c ub.conf dump_cache" -$PRE/unbound-control -c ub.conf dump_cache > tmp.$$ -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -cat tmp.$$ -if grep 10.20.30.40 tmp.$$; then - echo "OK example.com is in cache dump" -else - echo "Not OK cache dump" - exit 1 -fi +teststep "dump the cache" +query www.example.com. +cache_dump -c ub.conf +expect_exit_value 0 +cat cache.dump +expect_in_cache "10.20.30.40" -# test lookup -echo "$PRE/unbound-control -c ub.conf lookup www.example.com" -$PRE/unbound-control -c ub.conf lookup www.example.com -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +control_command -c ub.conf lookup www.example.com +expect_exit_value 0 # answer to lookup is meaningless because of use a forwarder, oh well. -# load the cache dump. -echo "$PRE/unbound-control -c ub.conf load_cache < tmp.$$" -$PRE/unbound-control -c ub.conf load_cache < tmp.$$ -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> dig www.example.com." -dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile -echo "> check answer" -if grep "10.20.30.40" outfile; then - echo "OK" -else - echo "> cat logfiles" - cat fwd.log - cat unbound.log - echo "Not OK" - exit 1 -fi +teststep "load the cache dump" +cache_load -c ub.conf +expect_exit_value 0 +query www.example.com. +expect_answer "10.20.30.40" -# load local-zones from file -echo "$PRE/unbound-control -c ub.conf local_zones < local_zones" -$PRE/unbound-control -c ub.conf local_zones < local_zones -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> dig localzonefromfile." -dig @127.0.0.1 -p $UNBOUND_PORT localzonefromfile | tee outfile -echo "> check answer" -if grep "REFUSED" outfile; then - echo "OK" -else - echo "Not OK" - exit 1 -fi +teststep "load local-zones from file" +control_command -c ub.conf local_zones < local_zones +expect_exit_value 0 +query localzonefromfile +expect_answer "REFUSED" -# load local-data from file -echo "$PRE/unbound-control -c ub.conf local_datas < local_data" -$PRE/unbound-control -c ub.conf local_datas < local_data -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> dig localdatafromfile." -dig @127.0.0.1 -p $UNBOUND_PORT -t txt localdatafromfile | tee outfile -echo "> check answer" -if grep "local data from file OK" outfile; then - echo "OK" -else - echo "Not OK" - exit 1 -fi +teststep "load local-data from file" +control_command -c ub.conf local_datas < local_data +expect_exit_value 0 +query -t txt localdatafromfile +expect_answer "local data from file OK" -# remove local-zone and local-data from file -echo "$PRE/unbound-control -c ub.conf local_zones_remove < local_zones_remove" -$PRE/unbound-control -c ub.conf local_zones_remove < local_zones_remove -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "$PRE/unbound-control -c ub.conf local_datas_remove < local_data_remove" -$PRE/unbound-control -c ub.conf local_datas_remove < local_data_remove -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -echo "> check zone and data removal list_local_zones" -$PRE/unbound-control -c ub.conf list_local_zones | tee outfile -if grep "localzonefromfile" outfile; then - echo "Not OK" - exit 1 -fi -if grep "local data from file OK" outfile; then - echo "Not OK" - exit 1 -fi -if grep "otherlocalzone" outfile; then - echo "OK" -else - echo "Not OK" - exit 1 -fi +teststep "remove local-zone and local-data from file" +control_command -c ub.conf local_zones_remove < local_zones_remove +expect_exit_value 0 +control_command -c ub.conf local_datas_remove < local_data_remove +expect_exit_value 0 +control_command -c ub.conf list_local_zones +fail_answer "localzonefromfile" +fail_answer "local data from file OK" +expect_answer "otherlocalzone" -# flushing -echo "$PRE/unbound-control -c ub.conf flush www.example.net" -$PRE/unbound-control -c ub.conf flush www.example.net -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +teststep "flushing" +control_command -c ub.conf flush www.example.net +expect_exit_value 0 +control_command -c ub.conf flush_type www.example.net TXT +expect_exit_value 0 +control_command -c ub.conf flush_zone example.net +expect_exit_value 0 -echo "$PRE/unbound-control -c ub.conf flush_type www.example.net TXT" -$PRE/unbound-control -c ub.conf flush_type www.example.net TXT -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +teststep "reload the server for a clean state and populate the cache" +cp main.conf ub.conf +control_command -c ub.conf reload +expect_exit_value 0 +query www.example.com +expect_answer "10.20.30.40" -echo "$PRE/unbound-control -c ub.conf flush_zone example.net" -$PRE/unbound-control -c ub.conf flush_zone example.net -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi +teststep "reload and check cache dump - should be empty" +control_command -c ub.conf reload +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +fail_in_cache_dump "www.example.com.*10.20.30.40" +fail_in_cache_dump "msg www.example.com. IN A" -# now stop the server -echo "$PRE/unbound-control -c ub.conf stop" -$PRE/unbound-control -c ub.conf stop -if test $? -ne 0; then - echo "wrong exit value after success" - exit 1 -fi -# see if the server has really exited. +query www.example.com +expect_answer "10.20.30.40" + +teststep "reload_keep_cache and check cache dump - should not be empty" +control_command -c ub.conf reload_keep_cache +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +expect_in_cache_dump "www.example.com.*10.20.30.40" +expect_in_cache_dump "msg www.example.com. IN A" +query www.example.com +nordflag +expect_answer "10.20.30.40" + +teststep "change msg-cache-size and reload_keep_cache - should be empty" +echo "server: msg-cache-size: 2m" >> ub.conf +control_command -c ub.conf reload_keep_cache +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +fail_in_cache_dump "www.example.com.*10.20.30.40" +fail_in_cache_dump "msg www.example.com. IN A" +query www.example.com +expect_answer "10.20.30.40" + +teststep "change rrset-cache-size and reload_keep_cache - should be empty" +echo "server: rrset-cache-size: 2m" >> ub.conf +control_command -c ub.conf reload_keep_cache +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +fail_in_cache_dump "www.example.com.*10.20.30.40" +fail_in_cache_dump "msg www.example.com. IN A" +query www.example.com +expect_answer "10.20.30.40" + +teststep "change num-threads and reload_keep_cache - should be empty" +echo "server: num-threads: 2" >> ub.conf +control_command -c ub.conf reload_keep_cache +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +fail_in_cache_dump "www.example.com.*10.20.30.40" +fail_in_cache_dump "msg www.example.com. IN A" +query www.example.com +expect_answer "10.20.30.40" + +teststep "change minimal-responses and reload_keep_cache - should not be empty" +echo "server: minimal-responses: no" >> ub.conf +control_command -c ub.conf reload_keep_cache +expect_exit_value 0 +cache_dump -c ub.conf +expect_exit_value 0 +expect_in_cache_dump "www.example.com.*10.20.30.40" +expect_in_cache_dump "msg www.example.com. IN A" + +teststep "now stop the server" +control_command -c ub.conf stop +expect_exit_value 0 + +teststep "see if the server has really exited" TRY_MAX=20 for (( try=0 ; try <= $TRY_MAX ; try++ )) ; do if kill -0 $UNBOUND_PID 2>&1 | tee tmp.$$; then @@ -379,11 +332,8 @@ for (( try=0 ; try <= $TRY_MAX ; try++ )) ; do done if kill -0 $UNBOUND_PID; then echo "still up!" - echo "> cat logfiles" - cat fwd.log - cat unbound.log echo "not stopped, failure" - exit 1 + end 1 else echo "stopped OK" @@ -392,15 +342,9 @@ else echo "lock-verify test worked." else echo "lock-verify test failed." - cat fwd.log - cat unbound.log - exit 1 + end 1 fi fi fi -echo "> cat logfiles" -cat fwd.log -cat unbound.log -echo "> OK" -exit 0 +end 0 diff --git a/testdata/09-unbound-control.tdir/conf.bad_credentials b/testdata/09-unbound-control.tdir/conf.bad_credentials new file mode 100644 index 000000000..11a131130 --- /dev/null +++ b/testdata/09-unbound-control.tdir/conf.bad_credentials @@ -0,0 +1,5 @@ +remote-control: + server-key-file: bad_server.key + server-cert-file: bad_server.pem + control-key-file: bad_control.key + control-cert-file: bad_control.pem diff --git a/testdata/09-unbound-control.tdir/conf.spoofed_credentials b/testdata/09-unbound-control.tdir/conf.spoofed_credentials new file mode 100644 index 000000000..25cb830dc --- /dev/null +++ b/testdata/09-unbound-control.tdir/conf.spoofed_credentials @@ -0,0 +1,5 @@ +remote-control: + server-key-file: unbound_server.key + server-cert-file: unbound_server.pem + control-key-file: bad_control.key + control-cert-file: bad_control.pem diff --git a/testdata/common.sh b/testdata/common.sh index a449f1a64..b0e66f8df 100644 --- a/testdata/common.sh +++ b/testdata/common.sh @@ -29,6 +29,7 @@ # wait_server_up_or_fail: wait for server to come up or print a failure string # skip_test x : print message and skip test (must be called in .pre) # kill_pid : kill a server, make sure and wait for it to go down. +# teststep : print the current test step in the output # print error and exit @@ -272,3 +273,8 @@ set_doxygen_path () { fi } +# Print the current test step in the output +teststep () { + echo + echo "STEP [ $1 ]" +}