1 /* 2 * testcode/testbound.c - test program for unbound. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 * 35 */ 36 /** 37 * \file 38 * Exits with code 1 on a failure. 0 if all unit tests are successful. 39 */ 40 41 #include "config.h" 42 #ifdef HAVE_TIME_H 43 # include <time.h> 44 #endif 45 #include "testcode/testpkts.h" 46 #include "testcode/replay.h" 47 #include "testcode/fake_event.h" 48 #include "daemon/remote.h" 49 #include "util/config_file.h" 50 #include "sldns/keyraw.h" 51 #include <ctype.h> 52 53 /** signal that this is a testbound compile */ 54 #define unbound_testbound 1 55 /** 56 * include the main program from the unbound daemon. 57 * rename main to daemon_main to call it 58 */ 59 #define main daemon_main 60 #include "daemon/unbound.c" 61 #undef main 62 63 /** maximum line length for lines in the replay file. */ 64 #define MAX_LINE_LEN 1024 65 /** config files (removed at exit) */ 66 static struct config_strlist* cfgfiles = NULL; 67 68 /** give commandline usage for testbound. */ 69 static void 70 testbound_usage() 71 { 72 printf("usage: testbound [options]\n"); 73 printf("\ttest the unbound daemon.\n"); 74 printf("-h this help\n"); 75 printf("-p file playback text file\n"); 76 printf("-2 detect SHA256 support (exit code 0 or 1)\n"); 77 printf("-g detect GOST support (exit code 0 or 1)\n"); 78 printf("-e detect ECDSA support (exit code 0 or 1)\n"); 79 printf("-s testbound self-test - unit test of testbound parts.\n"); 80 printf("-o str unbound commandline options separated by spaces.\n"); 81 printf("Version %s\n", PACKAGE_VERSION); 82 printf("BSD licensed, see LICENSE file in source package.\n"); 83 printf("Report bugs to %s.\n", PACKAGE_BUGREPORT); 84 } 85 86 /** Max number of arguments to pass to unbound. */ 87 #define MAXARG 100 88 89 /** 90 * Add options from string to passed argc. splits on whitespace. 91 * @param args: the option argument, "-v -p 12345" or so. 92 * @param pass_argc: ptr to the argc for unbound. Modified. 93 * @param pass_argv: the argv to pass to unbound. Modified. 94 */ 95 static void 96 add_opts(const char* args, int* pass_argc, char* pass_argv[]) 97 { 98 const char *p = args, *np; 99 size_t len; 100 while(p && isspace((unsigned char)*p)) 101 p++; 102 while(p && *p) { 103 /* find location of next string and length of this one */ 104 if((np = strchr(p, ' '))) 105 len = (size_t)(np-p); 106 else len = strlen(p); 107 /* allocate and copy option */ 108 if(*pass_argc >= MAXARG-1) 109 fatal_exit("too many arguments: '%s'", p); 110 pass_argv[*pass_argc] = (char*)malloc(len+1); 111 if(!pass_argv[*pass_argc]) 112 fatal_exit("add_opts: out of memory"); 113 memcpy(pass_argv[*pass_argc], p, len); 114 pass_argv[*pass_argc][len] = 0; 115 (*pass_argc)++; 116 /* go to next option */ 117 p = np; 118 while(p && isspace((unsigned char)*p)) 119 p++; 120 } 121 } 122 123 /** pretty print commandline for unbound in this test */ 124 static void 125 echo_cmdline(int argc, char* argv[]) 126 { 127 int i; 128 fprintf(stderr, "testbound is starting:"); 129 for(i=0; i<argc; i++) { 130 fprintf(stderr, " [%s]", argv[i]); 131 } 132 fprintf(stderr, "\n"); 133 } 134 135 /** spool autotrust file */ 136 static void 137 spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id) 138 { 139 char line[MAX_LINE_LEN]; 140 char* parse; 141 FILE* spool; 142 /* find filename for new file */ 143 while(isspace((unsigned char)*id)) 144 id++; 145 if(*id == '\0') 146 fatal_exit("AUTROTRUST_FILE must have id, line %d", *lineno); 147 id[strlen(id)-1]=0; /* remove newline */ 148 fake_temp_file("_auto_", id, line, sizeof(line)); 149 /* add option for the file */ 150 fprintf(cfg, "server: auto-trust-anchor-file: \"%s\"\n", line); 151 /* open file and spool to it */ 152 spool = fopen(line, "w"); 153 if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno)); 154 fprintf(stderr, "testbound is spooling key file: %s\n", line); 155 if(!cfg_strlist_insert(&cfgfiles, strdup(line))) 156 fatal_exit("out of memory"); 157 line[sizeof(line)-1] = 0; 158 while(fgets(line, MAX_LINE_LEN-1, in)) { 159 parse = line; 160 (*lineno)++; 161 while(isspace((unsigned char)*parse)) 162 parse++; 163 if(strncmp(parse, "AUTOTRUST_END", 13) == 0) { 164 fclose(spool); 165 return; 166 } 167 fputs(line, spool); 168 } 169 fatal_exit("no AUTOTRUST_END in input file"); 170 } 171 172 /** process config elements */ 173 static void 174 setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[]) 175 { 176 char configfile[MAX_LINE_LEN]; 177 char line[MAX_LINE_LEN]; 178 char* parse; 179 FILE* cfg; 180 fake_temp_file("_cfg", "", configfile, sizeof(configfile)); 181 add_opts("-c", pass_argc, pass_argv); 182 add_opts(configfile, pass_argc, pass_argv); 183 cfg = fopen(configfile, "w"); 184 if(!cfg) fatal_exit("could not open %s: %s", 185 configfile, strerror(errno)); 186 if(!cfg_strlist_insert(&cfgfiles, strdup(configfile))) 187 fatal_exit("out of memory"); 188 line[sizeof(line)-1] = 0; 189 /* some basic settings to not pollute the host system */ 190 fprintf(cfg, "server: use-syslog: no\n"); 191 fprintf(cfg, " directory: \"\"\n"); 192 fprintf(cfg, " chroot: \"\"\n"); 193 fprintf(cfg, " username: \"\"\n"); 194 fprintf(cfg, " pidfile: \"\"\n"); 195 fprintf(cfg, " val-log-level: 2\n"); 196 fprintf(cfg, "remote-control: control-enable: no\n"); 197 while(fgets(line, MAX_LINE_LEN-1, in)) { 198 parse = line; 199 (*lineno)++; 200 while(isspace((unsigned char)*parse)) 201 parse++; 202 if(!*parse || parse[0] == ';') 203 continue; 204 if(strncmp(parse, "COMMANDLINE", 11) == 0) { 205 parse[strlen(parse)-1] = 0; /* strip off \n */ 206 add_opts(parse+11, pass_argc, pass_argv); 207 continue; 208 } 209 if(strncmp(parse, "AUTOTRUST_FILE", 14) == 0) { 210 spool_auto_file(in, lineno, cfg, parse+14); 211 continue; 212 } 213 if(strncmp(parse, "CONFIG_END", 10) == 0) { 214 fclose(cfg); 215 return; 216 } 217 fputs(line, cfg); 218 } 219 fatal_exit("No CONFIG_END in input file"); 220 221 } 222 223 /** read playback file */ 224 static struct replay_scenario* 225 setup_playback(const char* filename, int* pass_argc, char* pass_argv[]) 226 { 227 struct replay_scenario* scen = NULL; 228 int lineno = 0; 229 230 if(filename) { 231 FILE *in = fopen(filename, "rb"); 232 if(!in) { 233 perror(filename); 234 exit(1); 235 } 236 setup_config(in, &lineno, pass_argc, pass_argv); 237 scen = replay_scenario_read(in, filename, &lineno); 238 fclose(in); 239 if(!scen) 240 fatal_exit("Could not read: %s", filename); 241 } 242 else fatal_exit("need a playback file (-p)"); 243 log_info("Scenario: %s", scen->title); 244 return scen; 245 } 246 247 /** remove config file at exit */ 248 void remove_configfile(void) 249 { 250 struct config_strlist* p; 251 for(p=cfgfiles; p; p=p->next) 252 unlink(p->str); 253 config_delstrlist(cfgfiles); 254 cfgfiles = NULL; 255 } 256 257 /** 258 * Main fake event test program. Setup, teardown and report errors. 259 * @param argc: arg count. 260 * @param argv: array of commandline arguments. 261 * @return program failure if test fails. 262 */ 263 int 264 main(int argc, char* argv[]) 265 { 266 int c, res; 267 int pass_argc = 0; 268 char* pass_argv[MAXARG]; 269 char* playback_file = NULL; 270 int init_optind = optind; 271 char* init_optarg = optarg; 272 struct replay_scenario* scen = NULL; 273 274 /* we do not want the test to depend on the timezone */ 275 (void)putenv("TZ=UTC"); 276 277 log_init(NULL, 0, NULL); 278 /* determine commandline options for the daemon */ 279 pass_argc = 1; 280 pass_argv[0] = "unbound"; 281 add_opts("-d", &pass_argc, pass_argv); 282 while( (c=getopt(argc, argv, "2egho:p:s")) != -1) { 283 switch(c) { 284 case 's': 285 free(pass_argv[1]); 286 testbound_selftest(); 287 exit(0); 288 case '2': 289 #if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2) 290 printf("SHA256 supported\n"); 291 exit(0); 292 #else 293 printf("SHA256 not supported\n"); 294 exit(1); 295 #endif 296 break; 297 case 'e': 298 #if defined(USE_ECDSA) 299 printf("ECDSA supported\n"); 300 exit(0); 301 #else 302 printf("ECDSA not supported\n"); 303 exit(1); 304 #endif 305 break; 306 case 'g': 307 #ifdef USE_GOST 308 if(sldns_key_EVP_load_gost_id()) { 309 printf("GOST supported\n"); 310 exit(0); 311 } else { 312 printf("GOST not supported\n"); 313 exit(1); 314 } 315 #else 316 printf("GOST not supported\n"); 317 exit(1); 318 #endif 319 break; 320 case 'p': 321 playback_file = optarg; 322 break; 323 case 'o': 324 add_opts(optarg, &pass_argc, pass_argv); 325 break; 326 case '?': 327 case 'h': 328 default: 329 testbound_usage(); 330 return 1; 331 } 332 } 333 argc -= optind; 334 argv += optind; 335 if(argc != 0) { 336 testbound_usage(); 337 return 1; 338 } 339 log_info("Start of %s testbound program.", PACKAGE_STRING); 340 if(atexit(&remove_configfile) != 0) 341 fatal_exit("atexit() failed: %s", strerror(errno)); 342 343 /* setup test environment */ 344 scen = setup_playback(playback_file, &pass_argc, pass_argv); 345 /* init fake event backend */ 346 fake_event_init(scen); 347 348 pass_argv[pass_argc] = NULL; 349 echo_cmdline(pass_argc, pass_argv); 350 351 /* reset getopt processing */ 352 optind = init_optind; 353 optarg = init_optarg; 354 355 /* run the normal daemon */ 356 res = daemon_main(pass_argc, pass_argv); 357 358 fake_event_cleanup(); 359 for(c=1; c<pass_argc; c++) 360 free(pass_argv[c]); 361 if(res == 0) { 362 log_info("Testbound Exit Success"); 363 #ifdef HAVE_PTHREAD 364 /* dlopen frees its thread state (dlopen of gost engine) */ 365 pthread_exit(NULL); 366 #endif 367 } 368 return res; 369 } 370 371 /* fake remote control */ 372 struct listen_port* daemon_remote_open_ports(struct config_file* 373 ATTR_UNUSED(cfg)) 374 { 375 return NULL; 376 } 377 378 struct daemon_remote* daemon_remote_create(struct config_file* ATTR_UNUSED(cfg)) 379 { 380 return (struct daemon_remote*)calloc(1,1); 381 } 382 383 void daemon_remote_delete(struct daemon_remote* rc) 384 { 385 free(rc); 386 } 387 388 void daemon_remote_clear(struct daemon_remote* ATTR_UNUSED(rc)) 389 { 390 /* nothing */ 391 } 392 393 int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc), 394 struct listen_port* ATTR_UNUSED(ports), 395 struct worker* ATTR_UNUSED(worker)) 396 { 397 return 1; 398 } 399 400 int remote_accept_callback(struct comm_point* ATTR_UNUSED(c), 401 void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), 402 struct comm_reply* ATTR_UNUSED(repinfo)) 403 { 404 log_assert(0); 405 return 0; 406 } 407 408 int remote_control_callback(struct comm_point* ATTR_UNUSED(c), 409 void* ATTR_UNUSED(arg), int ATTR_UNUSED(error), 410 struct comm_reply* ATTR_UNUSED(repinfo)) 411 { 412 log_assert(0); 413 return 0; 414 } 415 416 void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg)) 417 { 418 log_assert(0); 419 } 420 421 void wsvc_command_option(const char* ATTR_UNUSED(wopt), 422 const char* ATTR_UNUSED(cfgfile), int ATTR_UNUSED(v), 423 int ATTR_UNUSED(c)) 424 { 425 log_assert(0); 426 } 427 428 void wsvc_setup_worker(struct worker* ATTR_UNUSED(worker)) 429 { 430 /* do nothing */ 431 } 432 433 void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker)) 434 { 435 /* do nothing */ 436 } 437 438 #ifdef UB_ON_WINDOWS 439 void worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), 440 void* ATTR_UNUSED(arg)) 441 { 442 log_assert(0); 443 } 444 445 void wsvc_cron_cb(void* ATTR_UNUSED(arg)) 446 { 447 log_assert(0); 448 } 449 #endif /* UB_ON_WINDOWS */ 450 451