/* * testcode/testbound.c - test program for unbound. * * Copyright (c) 2007, NLnet Labs. All rights reserved. * * This software is open source. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the NLNET LABS nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** * \file * Exits with code 1 on a failure. 0 if all unit tests are successful. */ #include "config.h" #ifdef HAVE_TIME_H # include #endif #include "testcode/testpkts.h" #include "testcode/replay.h" #include "testcode/fake_event.h" #include "daemon/remote.h" #include "util/config_file.h" #include "sldns/keyraw.h" #include /** signal that this is a testbound compile */ #define unbound_testbound 1 /** * include the main program from the unbound daemon. * rename main to daemon_main to call it */ #define main daemon_main #include "daemon/unbound.c" #undef main /** maximum line length for lines in the replay file. */ #define MAX_LINE_LEN 1024 /** config files (removed at exit) */ static struct config_strlist* cfgfiles = NULL; #ifdef UNBOUND_ALLOC_STATS # define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__) char* unbound_stat_strdup_log(char* s, const char* file, int line, const char* func); char* unbound_stat_strdup_log(char* s, const char* file, int line, const char* func) { char* result; size_t len; if(!s) return NULL; len = strlen(s); log_info("%s:%d %s strdup(%u)", file, line, func, (unsigned)len+1); result = unbound_stat_malloc(len+1); memmove(result, s, len+1); return result; } #endif /* UNBOUND_ALLOC_STATS */ /** give commandline usage for testbound. */ static void testbound_usage(void) { printf("usage: testbound [options]\n"); printf("\ttest the unbound daemon.\n"); printf("-h this help\n"); printf("-p file playback text file\n"); printf("-1 detect SHA1 support (exit code 0 or 1)\n"); printf("-2 detect SHA256 support (exit code 0 or 1)\n"); printf("-g detect GOST support (exit code 0 or 1)\n"); printf("-e detect ECDSA support (exit code 0 or 1)\n"); printf("-c detect CLIENT_SUBNET support (exit code 0 or 1)\n"); printf("-i detect IPSECMOD support (exit code 0 or 1)\n"); printf("-s testbound self-test - unit test of testbound parts.\n"); printf("-o str unbound commandline options separated by spaces.\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE file in source package.\n"); printf("Report bugs to %s.\n", PACKAGE_BUGREPORT); } /** Max number of arguments to pass to unbound. */ #define MAXARG 100 /** * Add options from string to passed argc. splits on whitespace. * @param args: the option argument, "-v -p 12345" or so. * @param pass_argc: ptr to the argc for unbound. Modified. * @param pass_argv: the argv to pass to unbound. Modified. */ static void add_opts(const char* args, int* pass_argc, char* pass_argv[]) { const char *p = args, *np; size_t len; while(p && isspace((unsigned char)*p)) p++; while(p && *p) { /* find location of next string and length of this one */ if((np = strchr(p, ' '))) len = (size_t)(np-p); else len = strlen(p); /* allocate and copy option */ if(*pass_argc >= MAXARG-1) fatal_exit("too many arguments: '%s'", p); pass_argv[*pass_argc] = (char*)malloc(len+1); if(!pass_argv[*pass_argc]) fatal_exit("add_opts: out of memory"); memcpy(pass_argv[*pass_argc], p, len); pass_argv[*pass_argc][len] = 0; (*pass_argc)++; /* go to next option */ p = np; while(p && isspace((unsigned char)*p)) p++; } } /** pretty print commandline for unbound in this test */ static void echo_cmdline(int argc, char* argv[]) { int i; fprintf(stderr, "testbound is starting:"); for(i=0; ititle); return scen; } /** remove config file at exit */ void remove_configfile(void) { struct config_strlist* p; for(p=cfgfiles; p; p=p->next) unlink(p->str); config_delstrlist(cfgfiles); cfgfiles = NULL; } /** * Main fake event test program. Setup, teardown and report errors. * @param argc: arg count. * @param argv: array of commandline arguments. * @return program failure if test fails. */ int main(int argc, char* argv[]) { int c, res; int pass_argc = 0; char* pass_argv[MAXARG]; char* playback_file = NULL; int init_optind = optind; char* init_optarg = optarg; struct replay_scenario* scen = NULL; /* we do not want the test to depend on the timezone */ (void)putenv("TZ=UTC"); memset(pass_argv, 0, sizeof(pass_argv)); log_init(NULL, 0, NULL); /* determine commandline options for the daemon */ pass_argc = 1; pass_argv[0] = "unbound"; add_opts("-d", &pass_argc, pass_argv); while( (c=getopt(argc, argv, "12egciho:p:s")) != -1) { switch(c) { case 's': free(pass_argv[1]); testbound_selftest(); checklock_stop(); if(log_get_lock()) { lock_basic_destroy((lock_basic_type*)log_get_lock()); } exit(0); case '1': #ifdef USE_SHA1 printf("SHA1 supported\n"); exit(0); #else printf("SHA1 not supported\n"); exit(1); #endif break; case '2': #if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2) printf("SHA256 supported\n"); exit(0); #else printf("SHA256 not supported\n"); exit(1); #endif break; case 'e': #if defined(USE_ECDSA) printf("ECDSA supported\n"); exit(0); #else printf("ECDSA not supported\n"); exit(1); #endif break; case 'g': #ifdef USE_GOST if(sldns_key_EVP_load_gost_id()) { printf("GOST supported\n"); exit(0); } else { printf("GOST not supported\n"); exit(1); } #else printf("GOST not supported\n"); exit(1); #endif break; case 'c': #ifdef CLIENT_SUBNET printf("CLIENT_SUBNET supported\n"); exit(0); #else printf("CLIENT_SUBNET not supported\n"); exit(1); #endif break; case 'i': #ifdef USE_IPSECMOD printf("IPSECMOD supported\n"); exit(0); #else printf("IPSECMOD not supported\n"); exit(1); #endif break; case 'p': playback_file = optarg; break; case 'o': add_opts(optarg, &pass_argc, pass_argv); break; case '?': case 'h': default: testbound_usage(); exit(1); } } argc -= optind; /* argv += optind; not using further arguments */ if(argc != 0) { testbound_usage(); exit(1); } log_info("Start of %s testbound program.", PACKAGE_STRING); if(atexit(&remove_configfile) != 0) fatal_exit("atexit() failed: %s", strerror(errno)); /* setup test environment */ scen = setup_playback(playback_file, &pass_argc, pass_argv); /* init fake event backend */ fake_event_init(scen); pass_argv[pass_argc] = NULL; echo_cmdline(pass_argc, pass_argv); /* reset getopt processing */ optind = init_optind; optarg = init_optarg; /* run the normal daemon */ res = daemon_main(pass_argc, pass_argv); fake_event_cleanup(); for(c=1; c