1/* 2 * Copyright (C) 2004, 2005, 2007-2010, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (C) 1999-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 * PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/* $Id$ */ 19 20/*! \file */ 21 22#include <config.h> 23 24#include <ctype.h> 25#include <errno.h> 26#include <limits.h> 27#include <signal.h> 28#include <stdarg.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <time.h> 32#include <unistd.h> 33 34#include <sys/wait.h> 35 36#include <isc/boolean.h> 37#include <isc/commandline.h> 38#include <isc/print.h> 39#include <isc/string.h> 40#include <isc/mem.h> 41 42#include <dns/compress.h> 43#include <dns/result.h> 44 45#include "include/tests/t_api.h" 46 47static const char *Usage = 48 "\t-a : run all tests\n" 49 "\t-b <dir> : chdir to dir before running tests" 50 "\t-c <config_file> : use specified config file\n" 51 "\t-d <debug_level> : set debug level to debug_level\n" 52 "\t-h : print test info\n" 53 "\t-u : print usage info\n" 54 "\t-n <test_name> : run specified test name\n" 55 "\t-t <test_number> : run specified test number\n" 56 "\t-x : don't execute tests in a subproc\n" 57 "\t-q <timeout> : use 'timeout' as the timeout value\n"; 58/*!< 59 * -a --> run all tests 60 * -b dir --> chdir to dir before running tests 61 * -c config --> use config file 'config' 62 * -d --> turn on api debugging 63 * -h --> print out available test names 64 * -u --> print usage info 65 * -n name --> run test named name 66 * -tn --> run test n 67 * -x --> don't execute testcases in a subproc 68 * -q timeout --> use 'timeout' as the timeout value 69 */ 70 71#define T_MAXTESTS 256 /*% must be 0 mod 8 */ 72#define T_MAXENV 256 73#define T_DEFAULT_CONFIG "t_config" 74#define T_BUFSIZ 256 75#define T_BIGBUF 4096 76 77#define T_TCTOUT 60 78 79int T_debug; 80int T_timeout; 81pid_t T_pid; 82static const char * T_config; 83static char T_tvec[T_MAXTESTS / 8]; 84static char * T_env[T_MAXENV + 1]; 85static char T_buf[T_BIGBUF]; 86static char * T_dir; 87 88static int 89t_initconf(const char *path); 90 91static int 92t_dumpconf(const char *path); 93 94static int 95t_putinfo(const char *key, const char *info); 96 97static char * 98t_getdate(char *buf, size_t buflen); 99 100static void 101printhelp(void); 102 103static void 104printusage(void); 105 106static int T_int; 107 108static void 109t_sighandler(int sig) { 110 T_int = sig; 111} 112 113int 114main(int argc, char **argv) { 115 int c; 116 int tnum; 117 int subprocs; 118 pid_t deadpid; 119 int status; 120 int len; 121 isc_boolean_t first; 122 testspec_t *pts; 123 struct sigaction sa; 124 125 isc_mem_debugging = ISC_MEM_DEBUGRECORD; 126 first = ISC_TRUE; 127 subprocs = 1; 128 T_timeout = T_TCTOUT; 129 130 /* 131 * -a option is now default. 132 */ 133 memset(T_tvec, 0xff, sizeof(T_tvec)); 134 135 /* 136 * Parse args. 137 */ 138 while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) 139 != -1) { 140 if (c == 'a') { 141 /* 142 * Flag all tests to be run. 143 */ 144 memset(T_tvec, 0xff, sizeof(T_tvec)); 145 } 146 else if (c == 'b') { 147 T_dir = isc_commandline_argument; 148 } 149 else if (c == 't') { 150 tnum = atoi(isc_commandline_argument); 151 if ((tnum > 0) && (tnum < T_MAXTESTS)) { 152 if (first) { 153 /* 154 * Turn off effect of -a default 155 * and allow multiple -t and -n 156 * options. 157 */ 158 memset(T_tvec, 0, sizeof(T_tvec)); 159 first = ISC_FALSE; 160 } 161 /* 162 * Flag test tnum to be run. 163 */ 164 tnum -= 1; 165 T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); 166 } 167 } 168 else if (c == 'c') { 169 T_config = isc_commandline_argument; 170 } 171 else if (c == 'd') { 172 T_debug = atoi(isc_commandline_argument); 173 } 174 else if (c == 'n') { 175 pts = &T_testlist[0]; 176 tnum = 0; 177 while (pts->pfv != NULL) { 178 if (! strcmp(pts->func_name, 179 isc_commandline_argument)) { 180 if (first) { 181 memset(T_tvec, 0, 182 sizeof(T_tvec)); 183 first = ISC_FALSE; 184 } 185 T_tvec[tnum/8] |= (0x01 << (tnum%8)); 186 break; 187 } 188 ++pts; 189 ++tnum; 190 } 191 if (pts->pfv == NULL) { 192 fprintf(stderr, "no such test %s\n", 193 isc_commandline_argument); 194 exit(1); 195 } 196 } 197 else if (c == 'h') { 198 printhelp(); 199 exit(0); 200 } 201 else if (c == 'u') { 202 printusage(); 203 exit(0); 204 } 205 else if (c == 'x') { 206 subprocs = 0; 207 } 208 else if (c == 'q') { 209 T_timeout = atoi(isc_commandline_argument); 210 } 211 else if (c == ':') { 212 fprintf(stderr, "Option -%c requires an argument\n", 213 isc_commandline_option); 214 exit(1); 215 } 216 else if (c == '?') { 217 fprintf(stderr, "Unrecognized option -%c\n", 218 isc_commandline_option); 219 exit(1); 220 } 221 } 222 223 /* 224 * Set cwd. 225 */ 226 227 if (T_dir != NULL && chdir(T_dir) != 0) { 228 fprintf(stderr, "chdir %s failed\n", T_dir); 229 exit(1); 230 } 231 232 /* 233 * We don't want buffered output. 234 */ 235 236 (void)setbuf(stdout, NULL); 237 (void)setbuf(stderr, NULL); 238 239 /* 240 * Setup signals. 241 */ 242 243 sa.sa_flags = 0; 244 sigfillset(&sa.sa_mask); 245 246 sa.sa_handler = t_sighandler; 247 (void)sigaction(SIGINT, &sa, NULL); 248 (void)sigaction(SIGALRM, &sa, NULL); 249 250 /* 251 * Output start stanza to journal. 252 */ 253 254 snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); 255 len = strlen(T_buf); 256 (void) t_getdate(T_buf + len, T_BIGBUF - len); 257 t_putinfo("S", T_buf); 258 259 /* 260 * Setup the test environment using the config file. 261 */ 262 263 if (T_config == NULL) 264 T_config = T_DEFAULT_CONFIG; 265 266 t_initconf(T_config); 267 if (T_debug) 268 t_dumpconf(T_config); 269 270 /* 271 * Now invoke all the test cases. 272 */ 273 274 tnum = 0; 275 pts = &T_testlist[0]; 276 while (*pts->pfv != NULL) { 277 if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { 278 if (subprocs) { 279 T_pid = fork(); 280 if (T_pid == 0) { 281 (*pts->pfv)(); 282 exit(0); 283 } else if (T_pid > 0) { 284 285 T_int = 0; 286 sa.sa_handler = t_sighandler; 287 (void)sigaction(SIGALRM, &sa, NULL); 288 alarm(T_timeout); 289 290 deadpid = (pid_t) -1; 291 while (deadpid != T_pid) { 292 deadpid = 293 waitpid(T_pid, &status, 0); 294 if (deadpid == T_pid) { 295 if (WIFSIGNALED(status)) { 296 if (WTERMSIG(status) == 297 SIGTERM) 298 t_info( 299 "the test case timed out\n"); 300 else 301 t_info( 302 "the test case caused exception %d\n", 303 WTERMSIG(status)); 304 t_result(T_UNRESOLVED); 305 } 306 } else if ((deadpid == -1) && 307 (errno == EINTR) && 308 T_int) { 309 kill(T_pid, SIGTERM); 310 T_int = 0; 311 } 312 else if ((deadpid == -1) && 313 ((errno == ECHILD) || 314 (errno == ESRCH))) 315 break; 316 } 317 318 alarm(0); 319 sa.sa_handler = SIG_IGN; 320 (void)sigaction(SIGALRM, &sa, NULL); 321 } else { 322 t_info("fork failed, errno == %d\n", 323 errno); 324 t_result(T_UNRESOLVED); 325 } 326 } 327 else { 328 (*pts->pfv)(); 329 } 330 } 331 ++pts; 332 ++tnum; 333 } 334 335 snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); 336 len = strlen(T_buf); 337 (void) t_getdate(T_buf + len, T_BIGBUF - len); 338 t_putinfo("E", T_buf); 339 340 return(0); 341} 342 343void 344t_assert(const char *component, int anum, int class, const char *what, ...) { 345 va_list args; 346 347 (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? 348 "A" : "C"); 349 350 /* 351 * Format text to a buffer. 352 */ 353 va_start(args, what); 354 (void)vsnprintf(T_buf, sizeof(T_buf), what, args); 355 va_end(args); 356 357 (void)t_putinfo("A", T_buf); 358 (void)printf("\n"); 359} 360 361void 362t_info(const char *format, ...) { 363 va_list args; 364 365 va_start(args, format); 366 (void) vsnprintf(T_buf, sizeof(T_buf), format, args); 367 va_end(args); 368 (void) t_putinfo("I", T_buf); 369} 370 371void 372t_result(int result) { 373 const char *p; 374 375 switch (result) { 376 case T_PASS: 377 p = "PASS"; 378 break; 379 case T_FAIL: 380 p = "FAIL"; 381 break; 382 case T_UNRESOLVED: 383 p = "UNRESOLVED"; 384 break; 385 case T_UNSUPPORTED: 386 p = "UNSUPPORTED"; 387 break; 388 case T_UNTESTED: 389 p = "UNTESTED"; 390 break; 391 case T_THREADONLY: 392 p = "THREADONLY"; 393 break; 394 case T_PKCS11ONLY: 395 p = "PKCS11ONLY"; 396 break; 397 default: 398 p = "UNKNOWN"; 399 break; 400 } 401 printf("R:%s\n", p); 402} 403 404char * 405t_getenv(const char *name) { 406 char *n; 407 char **p; 408 size_t len; 409 410 n = NULL; 411 if (name && *name) { 412 413 p = &T_env[0]; 414 len = strlen(name); 415 416 while (*p != NULL) { 417 if (strncmp(*p, name, len) == 0) { 418 if ( *(*p + len) == '=') { 419 n = *p + len + 1; 420 break; 421 } 422 } 423 ++p; 424 } 425 } 426 return(n); 427} 428 429/* 430 * 431 * Read in the config file at path, initializing T_env. 432 * 433 * note: no format checking for now ... 434 * 435 */ 436 437static int 438t_initconf(const char *path) { 439 440 int n; 441 int rval; 442 char **p; 443 FILE *fp; 444 445 rval = -1; 446 447 fp = fopen(path, "r"); 448 if (fp != NULL) { 449 n = 0; 450 p = &T_env[0]; 451 while (n < T_MAXENV) { 452 *p = t_fgetbs(fp); 453 if (*p == NULL) 454 break; 455 if ((**p == '#') || (strchr(*p, '=') == NULL)) { 456 /* 457 * Skip comments and other junk. 458 */ 459 (void)free(*p); 460 continue; 461 } 462 ++p; ++n; 463 } 464 (void)fclose(fp); 465 rval = 0; 466 } 467 468 return (rval); 469} 470 471/* 472 * 473 * Dump T_env to stdout. 474 * 475 */ 476 477static int 478t_dumpconf(const char *path) { 479 int rval; 480 char **p; 481 FILE *fp; 482 483 rval = -1; 484 fp = fopen(path, "r"); 485 if (fp != NULL) { 486 p = &T_env[0]; 487 while (*p != NULL) { 488 printf("C:%s\n", *p); 489 ++p; 490 } 491 (void) fclose(fp); 492 rval = 0; 493 } 494 return(rval); 495} 496 497/* 498 * 499 * Read a newline or EOF terminated string from fp. 500 * On success: 501 * return a malloc'd buf containing the string with 502 * the newline converted to a '\0'. 503 * On error: 504 * return NULL. 505 * 506 * Caller is responsible for freeing buf. 507 * 508 */ 509 510char * 511t_fgetbs(FILE *fp) { 512 int c; 513 size_t n; 514 size_t size; 515 char *buf; 516 char *p; 517 518 n = 0; 519 size = T_BUFSIZ; 520 buf = (char *) malloc(T_BUFSIZ * sizeof(char)); 521 522 if (buf != NULL) { 523 p = buf; 524 while ((c = fgetc(fp)) != EOF) { 525 526 if (c == '\n') 527 break; 528 529 *p++ = c; 530 ++n; 531 if ( n >= size ) { 532 size += T_BUFSIZ; 533 buf = (char *)realloc(buf, 534 size * sizeof(char)); 535 if (buf == NULL) 536 break; 537 p = buf + n; 538 } 539 } 540 *p = '\0'; 541 if (c == EOF && n == 0U) { 542 free(buf); 543 return (NULL); 544 } 545 return (buf); 546 } else { 547 fprintf(stderr, "malloc failed %d", errno); 548 return(NULL); 549 } 550} 551 552/* 553 * 554 * Put info to log, using key. 555 * For now, just dump it out. 556 * Later format into pretty lines. 557 * 558 */ 559 560static int 561t_putinfo(const char *key, const char *info) { 562 int rval; 563 564 /* 565 * For now. 566 */ 567 rval = printf("%s:%s", key, info); 568 return(rval); 569} 570 571static char * 572t_getdate(char *buf, size_t buflen) { 573 size_t n; 574 time_t t; 575 struct tm *p; 576 577 t = time(NULL); 578 p = localtime(&t); 579 n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); 580 return(n != 0U ? buf : NULL); 581} 582 583/* 584 * Some generally used utilities. 585 */ 586struct dns_errormap { 587 isc_result_t result; 588 const char *text; 589} dns_errormap[] = { 590 { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, 591 { ISC_R_EXISTS, "ISC_R_EXISTS" }, 592 { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, 593 { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, 594 { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, 595 { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, 596 { ISC_R_RANGE, "ISC_R_RANGE" }, 597 { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, 598 { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, 599 /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ 600 /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ 601 { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, 602 { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, 603 { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, 604 { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, 605 { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, 606 { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, 607 { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, 608 { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, 609 { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, 610 { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, 611 { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, 612 { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, 613 { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, 614 { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, 615 { DNS_R_NOTTL, "DNS_R_NOTTL" }, 616 { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, 617 { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, 618 { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, 619 { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, 620 { DNS_R_BADTTL, "DNS_R_BADTTL" }, 621 { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, 622 { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, 623 { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, 624 { DNS_R_GLUE, "DNS_R_GLUE" }, 625 { DNS_R_DNAME, "DNS_R_DNAME" }, 626 { DNS_R_CNAME, "DNS_R_CNAME" }, 627 { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, 628 { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, 629 { DNS_R_BADDB, "DNS_R_BADDB" }, 630 { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, 631 { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, 632 { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, 633 { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, 634 { (isc_result_t)0, NULL } 635}; 636 637isc_result_t 638t_dns_result_fromtext(char *name) { 639 640 isc_result_t result; 641 struct dns_errormap *pmap; 642 643 result = ISC_R_UNEXPECTED; 644 645 pmap = dns_errormap; 646 while (pmap->text != NULL) { 647 if (strcmp(name, pmap->text) == 0) 648 break; 649 ++pmap; 650 } 651 652 if (pmap->text != NULL) 653 result = pmap->result; 654 655 return (result); 656} 657 658struct dc_method_map { 659 unsigned int dc_method; 660 const char *text; 661} dc_method_map[] = { 662 663 { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, 664 { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, 665 { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, 666 { 0, NULL } 667}; 668 669unsigned int 670t_dc_method_fromtext(char *name) { 671 unsigned int dc_method; 672 struct dc_method_map *pmap; 673 674 dc_method = DNS_COMPRESS_NONE; 675 676 pmap = dc_method_map; 677 while (pmap->text != NULL) { 678 if (strcmp(name, pmap->text) == 0) 679 break; 680 ++pmap; 681 } 682 683 if (pmap->text != NULL) 684 dc_method = pmap->dc_method; 685 686 return(dc_method); 687} 688 689int 690t_bustline(char *line, char **toks) { 691 int cnt; 692 char *p; 693 694 cnt = 0; 695 if (line && *line) { 696 while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { 697 *toks++ = p; 698 line = NULL; 699 ++cnt; 700 } 701 } 702 return(cnt); 703} 704 705static void 706printhelp(void) { 707 int cnt; 708 testspec_t *pts; 709 710 cnt = 1; 711 pts = &T_testlist[0]; 712 713 printf("Available tests:\n"); 714 while (pts->func_name) { 715 printf("\t%d\t%s\n", cnt, pts->func_name); 716 ++pts; 717 ++cnt; 718 } 719} 720 721static void 722printusage(void) { 723 printf("Usage:\n%s\n", Usage); 724} 725 726int 727t_eval(const char *filename, int (*func)(char **), int nargs) { 728 FILE *fp; 729 char *p; 730 int line; 731 int cnt; 732 int result; 733 int nfails; 734 int nprobs; 735 int npass; 736 char *tokens[T_MAXTOKS + 1]; 737 738 npass = 0; 739 nfails = 0; 740 nprobs = 0; 741 742 fp = fopen(filename, "r"); 743 if (fp != NULL) { 744 line = 0; 745 while ((p = t_fgetbs(fp)) != NULL) { 746 747 ++line; 748 749 /* 750 * Skip comment lines. 751 */ 752 if ((isspace((unsigned char)*p)) || (*p == '#')) { 753 (void)free(p); 754 continue; 755 } 756 757 cnt = t_bustline(p, tokens); 758 if (cnt == nargs) { 759 result = func(tokens); 760 switch (result) { 761 case T_PASS: 762 ++npass; 763 break; 764 case T_FAIL: 765 ++nfails; 766 break; 767 case T_UNTESTED: 768 break; 769 default: 770 ++nprobs; 771 break; 772 } 773 } else { 774 t_info("bad format in %s at line %d\n", 775 filename, line); 776 ++nprobs; 777 } 778 779 (void)free(p); 780 } 781 (void)fclose(fp); 782 } else { 783 t_info("Missing datafile %s\n", filename); 784 ++nprobs; 785 } 786 787 result = T_UNRESOLVED; 788 789 if (nfails == 0 && nprobs == 0 && npass > 0) 790 result = T_PASS; 791 else if (nfails > 0) 792 result = T_FAIL; 793 else if (npass == 0) 794 result = T_UNTESTED; 795 796 return (result); 797} 798