1 /* $NetBSD: queryperf.c,v 1.6 2014/12/10 04:37:56 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2000, 2001 Nominum, Inc. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 11 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 12 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 13 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 15 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 17 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /*** 21 *** DNS Query Performance Testing Tool (queryperf.c) 22 *** 23 *** Version Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp 24 *** 25 *** Stephen Jacob <sj@nominum.com> 26 ***/ 27 28 #define BIND_8_COMPAT /* Pull in <arpa/nameser_compat.h> */ 29 30 #include <sys/time.h> 31 #include <sys/types.h> 32 #include <sys/socket.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <limits.h> 37 #include <time.h> 38 #include <unistd.h> 39 #include <netdb.h> 40 #include <netinet/in.h> 41 #include <arpa/nameser.h> 42 #include <resolv.h> 43 #include <math.h> 44 #include <errno.h> 45 46 #ifdef HAVE_CONFIG_H 47 #include "config.h" 48 #ifndef HAVE_GETADDRINFO 49 #include "missing/addrinfo.h" 50 #endif 51 #endif 52 53 /* 54 * Configuration defaults 55 */ 56 57 #define DEF_MAX_QUERIES_OUTSTANDING 20 58 #define DEF_QUERY_TIMEOUT 5 /* in seconds */ 59 #define DEF_SERVER_TO_QUERY "127.0.0.1" 60 #define DEF_SERVER_PORT "53" 61 #define DEF_BUFFER_SIZE 32 /* in k */ 62 63 #define DEF_RTTARRAY_SIZE 50000 64 #define DEF_RTTARRAY_UNIT 100 /* in usec */ 65 66 /* 67 * Other constants / definitions 68 */ 69 70 #define COMMENT_CHAR ';' 71 #define CONFIG_CHAR '#' 72 #define MAX_PORT 65535 73 #define MAX_INPUT_LEN 512 74 #define MAX_DOMAIN_LEN 255 75 #define MAX_BUFFER_LEN 8192 /* in bytes */ 76 #define HARD_TIMEOUT_EXTRA 5 /* in seconds */ 77 #define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */ 78 #define EDNSLEN 11 79 #define DNS_HEADERLEN 12 80 81 #define FALSE 0 82 #define TRUE 1 83 84 #define WHITESPACE " \t\n" 85 86 enum directives_enum { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }; 87 #define DIRECTIVES { "server", "port", "maxqueries", "maxwait" } 88 #define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT } 89 90 #define QTYPE_STRINGS { \ 91 "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", \ 92 "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", "RP", \ 93 "AFSDB", "X25", "ISDN", "RT", "NSAP", "NSAP-PTR", "SIG", \ 94 "KEY", "PX", "GPOS", "AAAA", "LOC", "NXT", "EID", "NIMLOC", \ 95 "SRV", "ATMA", "NAPTR", "KX", "CERT", "A6", "DNAME", "SINK", \ 96 "OPT", "APL", "DS", "SSHFP", "IPSECKEY", "RRSIG", "NSEC", \ 97 "DNSKEY", "DHCID", "NSEC3", "NSEC3PARAM", "TLSA", "HIP", \ 98 "NINFO", "RKEY", "TALINK", "CDS", "SPF", "UINFO", "UID", \ 99 "GID", "UNSPEC", "NID", "L32", "L64", "LP", "TKEY", "TSIG", \ 100 "IXFR", "AXFR", "MAILB", "MAILA", "URI", "CAA", "*", "ANY", \ 101 "TA", "DLV" \ 102 } 103 104 #define QTYPE_CODES { \ 105 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, \ 106 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, \ 107 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, \ 108 49, 50, 51, 52, 55, 56, 57, 58, 59, 99, 100, 101, 102, 103, \ 109 104, 105, 106, 107, 249, 250, 251, 252, 253, 254, 255, 255, \ 110 256, 257, 32768, 32769 \ 111 } 112 113 #define RCODE_STRINGS { \ 114 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \ 115 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \ 116 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \ 117 "rcode12", "rcode13", "rcode14", "rcode15" \ 118 } 119 120 /* 121 * Data type definitions 122 */ 123 124 #define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */ 125 #define VALID_QUERY_STATUS(q) ((q) != NULL && \ 126 (q)->magic == QUERY_STATUS_MAGIC) 127 128 struct query_status { 129 unsigned int magic; 130 int in_use; 131 unsigned short int id; 132 struct timeval sent_timestamp; 133 char *desc; 134 int qtype; 135 char qname[MAX_DOMAIN_LEN + 1]; 136 }; 137 138 struct query_mininfo { /* minimum info for timeout queries */ 139 int qtype; /* use -1 if N/A */ 140 struct timeval sent_timestamp; 141 char qname[MAX_DOMAIN_LEN + 1]; 142 }; 143 144 /* 145 * Forward declarations. 146 */ 147 int is_uint(char *test_int, unsigned int *result); 148 149 /* 150 * Configuration options (global) 151 */ 152 153 unsigned int max_queries_outstanding; /* init 0 */ 154 unsigned int query_timeout = DEF_QUERY_TIMEOUT; 155 int ignore_config_changes = FALSE; 156 unsigned int socket_bufsize = DEF_BUFFER_SIZE; 157 158 int family = AF_UNSPEC; 159 int use_stdin = TRUE; 160 char *datafile_name; /* init NULL */ 161 162 char *server_to_query; /* init NULL */ 163 char *server_port; /* init NULL */ 164 struct addrinfo *server_ai; /* init NULL */ 165 166 int run_only_once = FALSE; 167 int use_timelimit = FALSE; 168 unsigned int run_timelimit; /* init 0 */ 169 unsigned int print_interval; /* init 0 */ 170 171 unsigned int target_qps; /* init 0 */ 172 173 int serverset = FALSE, portset = FALSE; 174 int queriesset = FALSE, timeoutset = FALSE; 175 int edns = FALSE, dnssec = FALSE; 176 int countrcodes = FALSE; 177 int rcodecounts[16] = {0}; 178 179 int verbose = FALSE; 180 int recurse = 1; 181 182 /* 183 * Other global stuff 184 */ 185 186 int setup_phase = TRUE; 187 188 FILE *datafile_ptr; /* init NULL */ 189 unsigned int runs_through_file; /* init 0 */ 190 191 unsigned int num_queries_sent; /* init 0 */ 192 unsigned int num_queries_sent_interval; 193 unsigned int num_queries_outstanding; /* init 0 */ 194 unsigned int num_queries_timed_out; /* init 0 */ 195 unsigned int num_queries_possiblydelayed; /* init 0 */ 196 unsigned int num_queries_timed_out_interval; 197 unsigned int num_queries_possiblydelayed_interval; 198 199 struct timeval time_of_program_start; 200 struct timeval time_of_first_query; 201 double time_of_first_query_sec; 202 struct timeval time_of_first_query_interval; 203 struct timeval time_of_end_of_run; 204 struct timeval time_of_stop_sending; 205 206 struct timeval time_of_queryset_start; 207 double query_interval; 208 struct timeval time_of_next_queryset; 209 210 double rtt_max = -1; 211 double rtt_max_interval = -1; 212 double rtt_min = -1; 213 double rtt_min_interval = -1; 214 double rtt_total; 215 double rtt_total_interval; 216 int rttarray_size = DEF_RTTARRAY_SIZE; 217 int rttarray_unit = DEF_RTTARRAY_UNIT; 218 unsigned int *rttarray = NULL; 219 unsigned int *rttarray_interval = NULL; 220 unsigned int rtt_overflows; 221 unsigned int rtt_overflows_interval; 222 unsigned int rtt_counted; 223 unsigned int rtt_counted_interval; 224 char *rtt_histogram_file = NULL; 225 226 struct query_status *status; /* init NULL */ 227 unsigned int query_status_allocated; /* init 0 */ 228 229 int query_socket = -1; 230 int socket4 = -1, socket6 = -1; 231 232 static char *rcode_strings[] = RCODE_STRINGS; 233 234 static struct query_mininfo *timeout_queries; 235 236 /* 237 * get_uint16: 238 * Get an unsigned short integer from a buffer (in network order) 239 */ 240 static unsigned short 241 get_uint16(unsigned char *buf) { 242 unsigned short ret; 243 244 ret = buf[0] * 256 + buf[1]; 245 246 return (ret); 247 } 248 249 /* 250 * show_startup_info: 251 * Show name/version 252 */ 253 void 254 show_startup_info(void) { 255 printf("\n" 256 "DNS Query Performance Testing Tool\n" 257 "Version: Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp \n" 258 "\n"); 259 } 260 261 /* 262 * show_usage: 263 * Print out usage/syntax information 264 */ 265 void 266 show_usage(void) { 267 fprintf(stderr, 268 "\n" 269 "Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n" 270 " [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n" 271 " [-i interval] [-r arraysize] [-u unit] [-H histfile]\n" 272 " [-T qps] [-e] [-D] [-R] [-c] [-v] [-h]\n" 273 " -d specifies the input data file (default: stdin)\n" 274 " -s sets the server to query (default: %s)\n" 275 " -p sets the port on which to query the server (default: %s)\n" 276 " -q specifies the maximum number of queries outstanding (default: %d)\n" 277 " -t specifies the timeout for query completion in seconds (default: %d)\n" 278 " -n causes configuration changes to be ignored\n" 279 " -l specifies how a limit for how long to run tests in seconds (no default)\n" 280 " -1 run through input only once (default: multiple iff limit given)\n" 281 " -b set input/output buffer size in kilobytes (default: %d k)\n" 282 " -i specifies interval of intermediate outputs in seconds (default: 0=none)\n" 283 " -f specify address family of DNS transport, inet or inet6 (default: any)\n" 284 " -r set RTT statistics array size (default: %d)\n" 285 " -u set RTT statistics time unit in usec (default: %d)\n" 286 " -H specifies RTT histogram data file (default: none)\n" 287 " -T specify the target qps (default: 0=unspecified)\n" 288 " -e enable EDNS 0\n" 289 " -D set the DNSSEC OK bit (implies EDNS)\n" 290 " -R disable recursion\n" 291 " -c print the number of packets with each rcode\n" 292 " -v verbose: report the RCODE of each response on stdout\n" 293 " -h print this usage\n" 294 "\n", 295 DEF_SERVER_TO_QUERY, DEF_SERVER_PORT, 296 DEF_MAX_QUERIES_OUTSTANDING, DEF_QUERY_TIMEOUT, 297 DEF_BUFFER_SIZE, DEF_RTTARRAY_SIZE, DEF_RTTARRAY_UNIT); 298 } 299 300 /* 301 * set_datafile: 302 * Set the datafile to read 303 * 304 * Return -1 on failure 305 * Return a non-negative integer otherwise 306 */ 307 int 308 set_datafile(char *new_file) { 309 char *dfname_tmp; 310 311 if ((new_file == NULL) || (new_file[0] == '\0')) { 312 fprintf(stderr, "Error: null datafile name\n"); 313 return (-1); 314 } 315 316 if ((dfname_tmp = malloc(strlen(new_file) + 1)) == NULL) { 317 fprintf(stderr, "Error allocating memory for datafile name: " 318 "%s\n", new_file); 319 return (-1); 320 } 321 322 free(datafile_name); 323 datafile_name = dfname_tmp; 324 325 strcpy(datafile_name, new_file); 326 use_stdin = FALSE; 327 328 return (0); 329 } 330 331 /* 332 * set_input_stdin: 333 * Set the input to be stdin (instead of a datafile) 334 */ 335 void 336 set_input_stdin(void) { 337 use_stdin = TRUE; 338 free(datafile_name); 339 datafile_name = NULL; 340 } 341 342 /* 343 * set_server: 344 * Set the server to be queried 345 * 346 * Return -1 on failure 347 * Return a non-negative integer otherwise 348 */ 349 int 350 set_server(char *new_name) { 351 static struct hostent *server_he; 352 353 /* If no change in server name, don't do anything... */ 354 if ((server_to_query != NULL) && (new_name != NULL)) 355 if (strcmp(new_name, server_to_query) == 0) 356 return (0); 357 358 if ((new_name == NULL) || (new_name[0] == '\0')) { 359 fprintf(stderr, "Error: null server name\n"); 360 return (-1); 361 } 362 363 free(server_to_query); 364 server_to_query = NULL; 365 366 if ((server_to_query = malloc(strlen(new_name) + 1)) == NULL) { 367 fprintf(stderr, "Error allocating memory for server name: " 368 "%s\n", new_name); 369 return (-1); 370 } 371 372 strcpy(server_to_query, new_name); 373 374 return (0); 375 } 376 377 /* 378 * set_server_port: 379 * Set the port on which to contact the server 380 * 381 * Return -1 if port is invalid 382 * Return a non-negative integer otherwise 383 */ 384 int 385 set_server_port(char *new_port) { 386 unsigned int uint_val; 387 388 if ((is_uint(new_port, &uint_val)) != TRUE) 389 return (-1); 390 391 if (uint_val && uint_val > MAX_PORT) 392 return (-1); 393 else { 394 if (server_port != NULL && new_port != NULL && 395 strcmp(server_port, new_port) == 0) 396 return (0); 397 398 free(server_port); 399 server_port = NULL; 400 401 if ((server_port = malloc(strlen(new_port) + 1)) == NULL) { 402 fprintf(stderr, 403 "Error allocating memory for server port: " 404 "%s\n", new_port); 405 return (-1); 406 } 407 408 strcpy(server_port, new_port); 409 410 return (0); 411 } 412 } 413 414 int 415 set_server_sa(void) { 416 struct addrinfo hints, *res; 417 static struct protoent *proto; 418 int error; 419 420 if (proto == NULL && (proto = getprotobyname("udp")) == NULL) { 421 fprintf(stderr, "Error: getprotobyname call failed"); 422 return (-1); 423 } 424 425 memset(&hints, 0, sizeof(hints)); 426 hints.ai_family = family; 427 hints.ai_socktype = SOCK_DGRAM; 428 hints.ai_protocol = proto->p_proto; 429 if ((error = getaddrinfo(server_to_query, server_port, 430 &hints, &res)) != 0) { 431 fprintf(stderr, "Error: getaddrinfo(%s, %s) failed\n", 432 server_to_query, server_port); 433 return (-1); 434 } 435 436 /* replace the server's addrinfo */ 437 if (server_ai != NULL) 438 freeaddrinfo(server_ai); 439 server_ai = res; 440 return (0); 441 } 442 443 /* 444 * is_digit: 445 * Tests if a character is a digit 446 * 447 * Return TRUE if it is 448 * Return FALSE if it is not 449 */ 450 int 451 is_digit(char d) { 452 if (d < '0' || d > '9') 453 return (FALSE); 454 else 455 return (TRUE); 456 } 457 458 /* 459 * is_uint: 460 * Tests if a string, test_int, is a valid unsigned integer 461 * 462 * Sets *result to be the unsigned integer if it is valid 463 * 464 * Return TRUE if it is 465 * Return FALSE if it is not 466 */ 467 int 468 is_uint(char *test_int, unsigned int *result) { 469 unsigned long int value; 470 char *end; 471 472 if (test_int == NULL) 473 return (FALSE); 474 475 if (is_digit(test_int[0]) == FALSE) 476 return (FALSE); 477 478 value = strtoul(test_int, &end, 10); 479 480 if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX)) 481 return (FALSE); 482 483 *result = (unsigned int)value; 484 return (TRUE); 485 } 486 487 /* 488 * set_max_queries: 489 * Set the maximum number of outstanding queries 490 * 491 * Returns -1 on failure 492 * Returns a non-negative integer otherwise 493 */ 494 int 495 set_max_queries(unsigned int new_max) { 496 static unsigned int size_qs = sizeof(struct query_status); 497 struct query_status *temp_stat; 498 unsigned int count; 499 500 if (new_max > query_status_allocated) { 501 temp_stat = realloc(status, new_max * size_qs); 502 503 if (temp_stat == NULL) { 504 fprintf(stderr, "Error resizing query_status\n"); 505 return (-1); 506 } else { 507 status = temp_stat; 508 509 /* 510 * Be careful to only initialise between above 511 * the previously allocated space. Note that the 512 * allocation may be larger than the current 513 * max_queries_outstanding. We don't want to 514 * "forget" any outstanding queries! We might 515 * still have some above the bounds of the max. 516 */ 517 count = query_status_allocated; 518 for (; count < new_max; count++) { 519 status[count].in_use = FALSE; 520 status[count].magic = QUERY_STATUS_MAGIC; 521 status[count].desc = NULL; 522 } 523 524 query_status_allocated = new_max; 525 } 526 } 527 528 max_queries_outstanding = new_max; 529 530 return (0); 531 } 532 533 /* 534 * parse_args: 535 * Parse program arguments and set configuration options 536 * 537 * Return -1 on failure 538 * Return a non-negative integer otherwise 539 */ 540 int 541 parse_args(int argc, char **argv) { 542 int c; 543 unsigned int uint_arg_val; 544 545 while ((c = getopt(argc, argv, 546 "f:q:t:i:nd:s:p:1l:b:eDcvr:RT:u:H:h")) != -1) { 547 switch (c) { 548 case 'f': 549 if (strcmp(optarg, "inet") == 0) 550 family = AF_INET; 551 #ifdef AF_INET6 552 else if (strcmp(optarg, "inet6") == 0) 553 family = AF_INET6; 554 #endif 555 else if (strcmp(optarg, "any") == 0) 556 family = AF_UNSPEC; 557 else { 558 fprintf(stderr, "Invalid address family: %s\n", 559 optarg); 560 return (-1); 561 } 562 break; 563 case 'q': 564 if (is_uint(optarg, &uint_arg_val) == TRUE) { 565 set_max_queries(uint_arg_val); 566 queriesset = TRUE; 567 } else { 568 fprintf(stderr, "Option requires a positive " 569 "integer value: -%c %s\n", 570 c, optarg); 571 return (-1); 572 } 573 break; 574 575 case 't': 576 if (is_uint(optarg, &uint_arg_val) == TRUE) { 577 query_timeout = uint_arg_val; 578 timeoutset = TRUE; 579 } else { 580 fprintf(stderr, "Option requires a positive " 581 "integer value: -%c %s\n", 582 c, optarg); 583 return (-1); 584 } 585 break; 586 587 case 'n': 588 ignore_config_changes = TRUE; 589 break; 590 591 case 'd': 592 if (set_datafile(optarg) == -1) { 593 fprintf(stderr, "Error setting datafile " 594 "name: %s\n", optarg); 595 return (-1); 596 } 597 break; 598 599 case 's': 600 if (set_server(optarg) == -1) { 601 fprintf(stderr, "Error setting server " 602 "name: %s\n", optarg); 603 return (-1); 604 } 605 serverset = TRUE; 606 break; 607 608 case 'p': 609 if (is_uint(optarg, &uint_arg_val) == TRUE && 610 uint_arg_val < MAX_PORT) 611 { 612 set_server_port(optarg); 613 portset = TRUE; 614 } else { 615 fprintf(stderr, "Option requires a positive " 616 "integer between 0 and %d: -%c %s\n", 617 MAX_PORT - 1, c, optarg); 618 return (-1); 619 } 620 break; 621 622 case '1': 623 run_only_once = TRUE; 624 break; 625 626 case 'l': 627 if (is_uint(optarg, &uint_arg_val) == TRUE) { 628 use_timelimit = TRUE; 629 run_timelimit = uint_arg_val; 630 } else { 631 fprintf(stderr, "Option requires a positive " 632 "integer: -%c %s\n", 633 c, optarg); 634 return (-1); 635 } 636 break; 637 638 case 'b': 639 if (is_uint(optarg, &uint_arg_val) == TRUE) { 640 socket_bufsize = uint_arg_val; 641 } else { 642 fprintf(stderr, "Option requires a positive " 643 "integer: -%c %s\n", 644 c, optarg); 645 return (-1); 646 } 647 break; 648 case 'e': 649 edns = TRUE; 650 break; 651 case 'D': 652 dnssec = TRUE; 653 edns = TRUE; 654 break; 655 case 'c': 656 countrcodes = TRUE; 657 break; 658 case 'v': 659 verbose = 1; 660 break; 661 case 'i': 662 if (is_uint(optarg, &uint_arg_val) == TRUE) 663 print_interval = uint_arg_val; 664 else { 665 fprintf(stderr, "Invalid interval: %s\n", 666 optarg); 667 return (-1); 668 } 669 break; 670 case 'R': 671 recurse = 0; 672 break; 673 case 'r': 674 if (is_uint(optarg, &uint_arg_val) == TRUE) 675 rttarray_size = uint_arg_val; 676 else { 677 fprintf(stderr, "Invalid RTT array size: %s\n", 678 optarg); 679 return (-1); 680 } 681 break; 682 case 'u': 683 if (is_uint(optarg, &uint_arg_val) == TRUE) 684 rttarray_unit = uint_arg_val; 685 else { 686 fprintf(stderr, "Invalid RTT unit: %s\n", 687 optarg); 688 return (-1); 689 } 690 break; 691 case 'H': 692 rtt_histogram_file = optarg; 693 break; 694 case 'T': 695 if (is_uint(optarg, &uint_arg_val) == TRUE) 696 target_qps = uint_arg_val; 697 else { 698 fprintf(stderr, "Invalid target qps: %s\n", 699 optarg); 700 return (-1); 701 } 702 break; 703 case 'h': 704 return (-1); 705 default: 706 fprintf(stderr, "Invalid option: -%c\n", optopt); 707 return (-1); 708 } 709 } 710 711 if (run_only_once == FALSE && use_timelimit == FALSE) 712 run_only_once = TRUE; 713 714 return (0); 715 } 716 717 /* 718 * open_datafile: 719 * Open the data file ready for reading 720 * 721 * Return -1 on failure 722 * Return non-negative integer on success 723 */ 724 int 725 open_datafile(void) { 726 if (use_stdin == TRUE) { 727 datafile_ptr = stdin; 728 return (0); 729 } else { 730 if ((datafile_ptr = fopen(datafile_name, "r")) == NULL) { 731 fprintf(stderr, "Error: unable to open datafile: %s\n", 732 datafile_name); 733 return (-1); 734 } else 735 return (0); 736 } 737 } 738 739 /* 740 * close_datafile: 741 * Close the data file if any is open 742 * 743 * Return -1 on failure 744 * Return non-negative integer on success, including if not needed 745 */ 746 int 747 close_datafile(void) { 748 if ((use_stdin == FALSE) && (datafile_ptr != NULL)) { 749 if (fclose(datafile_ptr) != 0) { 750 fprintf(stderr, "Error: unable to close datafile\n"); 751 return (-1); 752 } 753 } 754 755 return (0); 756 } 757 758 /* 759 * open_socket: 760 * Open a socket for the queries. When we have an active socket already, 761 * close it and open a new one. 762 * 763 * Return -1 on failure 764 * Return the socket identifier 765 */ 766 int 767 open_socket(void) { 768 int sock; 769 int ret; 770 int bufsize; 771 struct addrinfo hints, *res; 772 773 memset(&hints, 0, sizeof(hints)); 774 hints.ai_family = server_ai->ai_family; 775 hints.ai_socktype = server_ai->ai_socktype; 776 hints.ai_protocol = server_ai->ai_protocol; 777 hints.ai_flags = AI_PASSIVE; 778 779 if ((ret = getaddrinfo(NULL, "0", &hints, &res)) != 0) { 780 fprintf(stderr, 781 "Error: getaddrinfo for bind socket failed: %s\n", 782 gai_strerror(ret)); 783 return (-1); 784 } 785 786 if ((sock = socket(res->ai_family, SOCK_DGRAM, 787 res->ai_protocol)) == -1) { 788 fprintf(stderr, "Error: socket call failed"); 789 goto fail; 790 } 791 792 #if defined(AF_INET6) && defined(IPV6_V6ONLY) 793 if (res->ai_family == AF_INET6) { 794 int on = 1; 795 796 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, 797 &on, sizeof(on)) == -1) { 798 fprintf(stderr, 799 "Warning: setsockopt(IPV6_V6ONLY) failed\n"); 800 } 801 } 802 #endif 803 804 if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) 805 fprintf(stderr, "Error: bind call failed"); 806 807 freeaddrinfo(res); 808 809 bufsize = 1024 * socket_bufsize; 810 811 ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, 812 (char *) &bufsize, sizeof(bufsize)); 813 if (ret < 0) 814 fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n"); 815 816 ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, 817 (char *) &bufsize, sizeof(bufsize)); 818 if (ret < 0) 819 fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n"); 820 821 return (sock); 822 823 fail: 824 if (res) 825 freeaddrinfo(res); 826 return (-1); 827 } 828 829 /* 830 * close_socket: 831 * Close the query socket(s) 832 * 833 * Return -1 on failure 834 * Return a non-negative integer otherwise 835 */ 836 int 837 close_socket(void) { 838 if (socket4 != -1) { 839 if (close(socket4) != 0) { 840 fprintf(stderr, 841 "Error: unable to close IPv4 socket\n"); 842 return (-1); 843 } 844 } 845 846 if (socket6 != -1) { 847 if (close(socket6) != 0) { 848 fprintf(stderr, 849 "Error: unable to close IPv6 socket\n"); 850 return (-1); 851 } 852 } 853 854 query_socket = -1; 855 856 return (0); 857 } 858 859 /* 860 * change_socket: 861 * Choose an appropriate socket according to the address family of the 862 * current server. Open a new socket if necessary. 863 * 864 * Return -1 on failure 865 * Return the socket identifier 866 */ 867 int 868 change_socket(void) { 869 int s, *sockp; 870 871 switch (server_ai->ai_family) { 872 case AF_INET: 873 sockp = &socket4; 874 break; 875 #ifdef AF_INET6 876 case AF_INET6: 877 sockp = &socket6; 878 break; 879 #endif 880 default: 881 fprintf(stderr, "unexpected address family: %d\n", 882 server_ai->ai_family); 883 exit(1); 884 } 885 886 if (*sockp == -1) { 887 if ((s = open_socket()) == -1) 888 return (-1); 889 *sockp = s; 890 } 891 892 return (*sockp); 893 } 894 895 /* 896 * reset_rttarray: 897 * (re)allocate RTT array and zero-clear the whole buffer. 898 * if array is being used, it is freed. 899 * Returns -1 on failure 900 * Returns a non-negative integer otherwise 901 */ 902 int 903 reset_rttarray(int size) { 904 if (rttarray != NULL) 905 free(rttarray); 906 if (rttarray_interval != NULL) 907 free(rttarray_interval); 908 909 rttarray = NULL; 910 rttarray_interval = NULL; 911 rtt_max = -1; 912 rtt_min = -1; 913 914 if (size > 0) { 915 rttarray = malloc(size * sizeof(rttarray[0])); 916 if (rttarray == NULL) { 917 fprintf(stderr, 918 "Error: allocating memory for RTT array\n"); 919 return (-1); 920 } 921 memset(rttarray, 0, size * sizeof(rttarray[0])); 922 923 rttarray_interval = malloc(size * 924 sizeof(rttarray_interval[0])); 925 if (rttarray_interval == NULL) { 926 fprintf(stderr, 927 "Error: allocating memory for RTT array\n"); 928 return (-1); 929 } 930 931 memset(rttarray_interval, 0, 932 size * sizeof(rttarray_interval[0])); 933 } 934 935 return (0); 936 } 937 938 /* 939 * set_query_interval: 940 * set the interval of consecutive queries if the target qps are specified. 941 * Returns -1 on failure 942 * Returns a non-negative integer otherwise 943 */ 944 int 945 set_query_interval(unsigned int qps) { 946 if (qps == 0) 947 return (0); 948 949 query_interval = (1.0 / (double)qps); 950 951 return (0); 952 } 953 954 /* 955 * setup: 956 * Set configuration options from command line arguments 957 * Open datafile ready for reading 958 * 959 * Return -1 on failure 960 * Return non-negative integer on success 961 */ 962 int 963 setup(int argc, char **argv) { 964 set_input_stdin(); 965 966 if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING) == -1) { 967 fprintf(stderr, "%s: Unable to set default max outstanding " 968 "queries\n", argv[0]); 969 return (-1); 970 } 971 972 if (set_server(DEF_SERVER_TO_QUERY) == -1) { 973 fprintf(stderr, "%s: Error setting default server name\n", 974 argv[0]); 975 return (-1); 976 } 977 978 if (set_server_port(DEF_SERVER_PORT) == -1) { 979 fprintf(stderr, "%s: Error setting default server port\n", 980 argv[0]); 981 return (-1); 982 } 983 984 if (parse_args(argc, argv) == -1) { 985 show_usage(); 986 return (-1); 987 } 988 989 if (open_datafile() == -1) 990 return (-1); 991 992 if (set_server_sa() == -1) 993 return (-1); 994 995 if ((query_socket = change_socket()) == -1) 996 return (-1); 997 998 if (reset_rttarray(rttarray_size) == -1) 999 return (-1); 1000 1001 if (set_query_interval(target_qps) == -1) 1002 return (-1); 1003 1004 return (0); 1005 } 1006 1007 /* 1008 * set_timenow: 1009 * Set a timeval struct to indicate the current time 1010 */ 1011 void 1012 set_timenow(struct timeval *tv) { 1013 if (gettimeofday(tv, NULL) == -1) { 1014 fprintf(stderr, "Error in gettimeofday(). Using inaccurate " 1015 "time() instead\n"); 1016 tv->tv_sec = time(NULL); 1017 tv->tv_usec = 0; 1018 } 1019 } 1020 1021 /* 1022 * addtv: 1023 * add tv1 and tv2, store the result in tv_result. 1024 */ 1025 void 1026 addtv(struct timeval *tv1, struct timeval *tv2, struct timeval *tv_result) { 1027 tv_result->tv_sec = tv1->tv_sec + tv2->tv_sec; 1028 tv_result->tv_usec = tv1->tv_usec + tv2->tv_usec; 1029 if (tv_result->tv_usec > 1000000) { 1030 tv_result->tv_sec++; 1031 tv_result->tv_usec -= 1000000; 1032 } 1033 } 1034 1035 /* 1036 * difftv: 1037 * Find the difference in seconds between two timeval structs. 1038 * 1039 * Return the difference between tv1 and tv2 in seconds in a double. 1040 */ 1041 double 1042 difftv(struct timeval tv1, struct timeval tv2) { 1043 long diff_sec, diff_usec; 1044 double diff; 1045 1046 diff_sec = tv1.tv_sec - tv2.tv_sec; 1047 diff_usec = tv1.tv_usec - tv2.tv_usec; 1048 1049 diff = (double)diff_sec + ((double)diff_usec / 1000000.0); 1050 1051 return (diff); 1052 } 1053 1054 /* 1055 * timelimit_reached: 1056 * Have we reached the time limit (if any)? 1057 * 1058 * Returns FALSE if there is no time limit or if we have not reached it 1059 * Returns TRUE otherwise 1060 */ 1061 int 1062 timelimit_reached(void) { 1063 struct timeval time_now; 1064 1065 set_timenow(&time_now); 1066 1067 if (use_timelimit == FALSE) 1068 return (FALSE); 1069 1070 if (setup_phase == TRUE) { 1071 if (difftv(time_now, time_of_program_start) 1072 < (double)(run_timelimit + HARD_TIMEOUT_EXTRA)) 1073 return (FALSE); 1074 else 1075 return (TRUE); 1076 } else { 1077 if (difftv(time_now, time_of_first_query) 1078 < (double)run_timelimit) 1079 return (FALSE); 1080 else 1081 return (TRUE); 1082 } 1083 } 1084 1085 /* 1086 * keep_sending: 1087 * Should we keep sending queries or stop here? 1088 * 1089 * Return TRUE if we should keep on sending queries 1090 * Return FALSE if we should stop 1091 * 1092 * Side effects: 1093 * Rewinds the input and clears reached_end_input if we have reached the 1094 * end of the input, but we are meant to run through it multiple times 1095 * and have not hit the time limit yet (if any is set). 1096 */ 1097 int 1098 keep_sending(int *reached_end_input) { 1099 static int stop = FALSE; 1100 1101 if (stop == TRUE) 1102 return (FALSE); 1103 1104 if ((*reached_end_input == FALSE) && (timelimit_reached() == FALSE)) 1105 return (TRUE); 1106 else if ((*reached_end_input == TRUE) && (run_only_once == FALSE) 1107 && (timelimit_reached() == FALSE)) { 1108 rewind(datafile_ptr); 1109 *reached_end_input = FALSE; 1110 runs_through_file++; 1111 return (TRUE); 1112 } else { 1113 if (*reached_end_input == TRUE) 1114 runs_through_file++; 1115 set_timenow(&time_of_stop_sending); 1116 stop = TRUE; 1117 return (FALSE); 1118 } 1119 } 1120 1121 /* 1122 * queries_outstanding: 1123 * How many queries are outstanding? 1124 * 1125 * Returns the number of outstanding queries 1126 */ 1127 unsigned int 1128 queries_outstanding(void) { 1129 return (num_queries_outstanding); 1130 } 1131 1132 /* 1133 * next_input_line: 1134 * Get the next non-comment line from the input file 1135 * 1136 * Put text in line, up to max of n chars. Skip comment lines. 1137 * Skip empty lines. 1138 * 1139 * Return line length on success 1140 * Return 0 if cannot read a non-comment line (EOF or error) 1141 */ 1142 int 1143 next_input_line(char *line, int n) { 1144 char *result; 1145 1146 do { 1147 result = fgets(line, n, datafile_ptr); 1148 } while ((result != NULL) && 1149 ((line[0] == COMMENT_CHAR) || (line[0] == '\n'))); 1150 1151 if (result == NULL) 1152 return (0); 1153 else 1154 return (strlen(line)); 1155 } 1156 1157 /* 1158 * identify_directive: 1159 * Gives us a numerical value equivelant for a directive string 1160 * 1161 * Returns the value for the directive 1162 * Returns -1 if not a valid directive 1163 */ 1164 int 1165 identify_directive(char *dir) { 1166 static char *directives[] = DIRECTIVES; 1167 static int dir_values[] = DIR_VALUES; 1168 unsigned int index, num_directives; 1169 1170 num_directives = sizeof(directives) / sizeof(directives[0]); 1171 1172 if (num_directives > (sizeof(dir_values) / sizeof(int))) 1173 num_directives = sizeof(dir_values) / sizeof(int); 1174 1175 for (index = 0; index < num_directives; index++) { 1176 if (strcmp(dir, directives[index]) == 0) 1177 return (dir_values[index]); 1178 } 1179 1180 return (-1); 1181 } 1182 1183 /* 1184 * update_config: 1185 * Update configuration options from a line from the input file 1186 */ 1187 void 1188 update_config(char *config_change_desc) { 1189 char *directive, *config_value, *trailing_garbage; 1190 char conf_copy[MAX_INPUT_LEN + 1]; 1191 unsigned int uint_val; 1192 int directive_number; 1193 int check; 1194 int old_af; 1195 1196 if (ignore_config_changes == TRUE) { 1197 fprintf(stderr, "Ignoring configuration change: %s", 1198 config_change_desc); 1199 return; 1200 } 1201 1202 strcpy(conf_copy, config_change_desc); 1203 1204 ++config_change_desc; 1205 1206 if (*config_change_desc == '\0') { 1207 fprintf(stderr, "Invalid config: No directive present: %s\n", 1208 conf_copy); 1209 return; 1210 } 1211 1212 if (index(WHITESPACE, *config_change_desc) != NULL) { 1213 fprintf(stderr, "Invalid config: Space before directive or " 1214 "no directive present: %s\n", conf_copy); 1215 return; 1216 } 1217 1218 directive = strtok(config_change_desc, WHITESPACE); 1219 config_value = strtok(NULL, WHITESPACE); 1220 trailing_garbage = strtok(NULL, WHITESPACE); 1221 1222 if ((directive_number = identify_directive(directive)) == -1) { 1223 fprintf(stderr, "Invalid config: Bad directive: %s\n", 1224 conf_copy); 1225 return; 1226 } 1227 1228 if (config_value == NULL) { 1229 fprintf(stderr, "Invalid config: No value present: %s\n", 1230 conf_copy); 1231 return; 1232 } 1233 1234 if (trailing_garbage != NULL) { 1235 fprintf(stderr, "Config warning: " 1236 "trailing garbage: %s\n", conf_copy); 1237 } 1238 1239 switch(directive_number) { 1240 1241 case V_SERVER: 1242 if (serverset && (setup_phase == TRUE)) { 1243 fprintf(stderr, "Config change overridden by command " 1244 "line: %s\n", directive); 1245 return; 1246 } 1247 1248 if (set_server(config_value) == -1) { 1249 fprintf(stderr, "Set server error: unable to change " 1250 "the server name to '%s'\n", config_value); 1251 return; 1252 } 1253 1254 old_af = server_ai->ai_family; 1255 if (set_server_sa() == -1) { 1256 fprintf(stderr, "Set server error: unable to resolve " 1257 "a new server '%s'\n", 1258 config_value); 1259 return; 1260 } 1261 if (old_af != server_ai->ai_family) { 1262 if ((query_socket = change_socket()) == -1) { 1263 /* XXX: this is fatal */ 1264 fprintf(stderr, "Set server error: " 1265 "unable to open a new socket " 1266 "for '%s'\n", config_value); 1267 exit(1); 1268 } 1269 } 1270 1271 break; 1272 1273 case V_PORT: 1274 if (portset && (setup_phase == TRUE)) { 1275 fprintf(stderr, "Config change overridden by command " 1276 "line: %s\n", directive); 1277 return; 1278 } 1279 1280 check = is_uint(config_value, &uint_val); 1281 1282 if ((check == TRUE) && (uint_val > 0)) { 1283 if (set_server_port(config_value) == -1) { 1284 fprintf(stderr, "Invalid config: Bad value for" 1285 " %s: %s\n", directive, config_value); 1286 } else { 1287 if (set_server_sa() == -1) { 1288 fprintf(stderr, 1289 "Failed to set a new port\n"); 1290 return; 1291 } 1292 } 1293 } else 1294 fprintf(stderr, "Invalid config: Bad value for " 1295 "%s: %s\n", directive, config_value); 1296 break; 1297 1298 case V_MAXQUERIES: 1299 if (queriesset && (setup_phase == TRUE)) { 1300 fprintf(stderr, "Config change overridden by command " 1301 "line: %s\n", directive); 1302 return; 1303 } 1304 1305 check = is_uint(config_value, &uint_val); 1306 1307 if ((check == TRUE) && (uint_val > 0)) { 1308 set_max_queries(uint_val); 1309 } else 1310 fprintf(stderr, "Invalid config: Bad value for " 1311 "%s: %s\n", directive, config_value); 1312 break; 1313 1314 case V_MAXWAIT: 1315 if (timeoutset && (setup_phase == TRUE)) { 1316 fprintf(stderr, "Config change overridden by command " 1317 "line: %s\n", directive); 1318 return; 1319 } 1320 1321 check = is_uint(config_value, &uint_val); 1322 1323 if ((check == TRUE) && (uint_val > 0)) { 1324 query_timeout = uint_val; 1325 } else 1326 fprintf(stderr, "Invalid config: Bad value for " 1327 "%s: %s\n", directive, config_value); 1328 break; 1329 1330 default: 1331 fprintf(stderr, "Invalid config: Bad directive: %s\n", 1332 directive); 1333 break; 1334 } 1335 } 1336 1337 /* 1338 * parse_query: 1339 * Parse a query line from the input file 1340 * 1341 * Set qname to be the domain to query (up to a max of qnlen chars) 1342 * Set qtype to be the type of the query 1343 * 1344 * Return -1 on failure 1345 * Return a non-negative integer otherwise 1346 */ 1347 int 1348 parse_query(char *input, char *qname, unsigned int qnlen, int *qtype) { 1349 static char *qtype_strings[] = QTYPE_STRINGS; 1350 static int qtype_codes[] = QTYPE_CODES; 1351 unsigned int num_types, index; 1352 int found = FALSE; 1353 char incopy[MAX_INPUT_LEN + 1]; 1354 char *domain_str, *type_str; 1355 1356 num_types = sizeof(qtype_strings) / sizeof(qtype_strings[0]); 1357 if (num_types > (sizeof(qtype_codes) / sizeof(int))) 1358 num_types = sizeof(qtype_codes) / sizeof(int); 1359 1360 strcpy(incopy, input); 1361 1362 domain_str = strtok(incopy, WHITESPACE); 1363 type_str = strtok(NULL, WHITESPACE); 1364 1365 if ((domain_str == NULL) || (type_str == NULL)) { 1366 fprintf(stderr, "Invalid query input format: %s\n", input); 1367 return (-1); 1368 } 1369 1370 if (strlen(domain_str) > qnlen) { 1371 fprintf(stderr, "Query domain too long: %s\n", domain_str); 1372 return (-1); 1373 } 1374 1375 for (index = 0; (index < num_types) && (found == FALSE); index++) { 1376 if (strcasecmp(type_str, qtype_strings[index]) == 0) { 1377 *qtype = qtype_codes[index]; 1378 found = TRUE; 1379 } 1380 } 1381 1382 if (found == FALSE) { 1383 fprintf(stderr, "Query type not understood: %s\n", type_str); 1384 return (-1); 1385 } 1386 1387 strcpy(qname, domain_str); 1388 1389 return (0); 1390 } 1391 1392 /* 1393 * dispatch_query: 1394 * Send the query packet for the entry 1395 * 1396 * Return -1 on failure 1397 * Return a non-negative integer otherwise 1398 */ 1399 int 1400 dispatch_query(unsigned short int id, char *dom, int qt, u_char **pktp, 1401 int *pktlenp) 1402 { 1403 static u_char packet_buffer[PACKETSZ + 1]; 1404 int buffer_len = PACKETSZ; 1405 int bytes_sent; 1406 unsigned short int net_id = htons(id); 1407 char *id_ptr = (char *)&net_id; 1408 HEADER *hp = (HEADER *)packet_buffer; 1409 1410 buffer_len = res_mkquery(QUERY, dom, C_IN, qt, NULL, 0, 1411 NULL, packet_buffer, PACKETSZ); 1412 if (buffer_len == -1) { 1413 fprintf(stderr, "Failed to create query packet: %s %d\n", 1414 dom, qt); 1415 return (-1); 1416 } 1417 hp->rd = recurse; 1418 if (edns) { 1419 unsigned char *p; 1420 if (buffer_len + EDNSLEN >= PACKETSZ) { 1421 fprintf(stderr, "Failed to add OPT to query packet\n"); 1422 return (-1); 1423 } 1424 packet_buffer[11] = 1; 1425 p = &packet_buffer[buffer_len]; 1426 *p++ = 0; /* root name */ 1427 *p++ = 0; 1428 *p++ = 41; /* OPT */ 1429 *p++ = 16; 1430 *p++ = 0; /* UDP payload size (4K) */ 1431 *p++ = 0; /* extended rcode */ 1432 *p++ = 0; /* version */ 1433 if (dnssec) 1434 *p++ = 0x80; /* upper flag bits - DO set */ 1435 else 1436 *p++ = 0; /* upper flag bits */ 1437 *p++ = 0; /* lower flag bit */ 1438 *p++ = 0; 1439 *p++ = 0; /* rdlen == 0 */ 1440 buffer_len += EDNSLEN; 1441 } 1442 1443 packet_buffer[0] = id_ptr[0]; 1444 packet_buffer[1] = id_ptr[1]; 1445 1446 bytes_sent = sendto(query_socket, packet_buffer, buffer_len, 0, 1447 server_ai->ai_addr, server_ai->ai_addrlen); 1448 if (bytes_sent == -1) { 1449 fprintf(stderr, "Failed to send query packet: %s %d\n", 1450 dom, qt); 1451 return (-1); 1452 } 1453 1454 if (bytes_sent != buffer_len) 1455 fprintf(stderr, "Warning: incomplete packet sent: %s %d\n", 1456 dom, qt); 1457 1458 *pktp = packet_buffer; 1459 *pktlenp = buffer_len; 1460 1461 return (0); 1462 } 1463 1464 /* 1465 * send_query: 1466 * Send a query based on a line of input 1467 */ 1468 void 1469 send_query(char *query_desc) { 1470 static unsigned short int use_query_id = 0; 1471 static int qname_len = MAX_DOMAIN_LEN; 1472 static char domain[MAX_DOMAIN_LEN + 1]; 1473 u_char *qpkt; 1474 char serveraddr[NI_MAXHOST]; 1475 int query_type, qpkt_len; 1476 unsigned int count; 1477 1478 use_query_id++; 1479 1480 if (parse_query(query_desc, domain, qname_len, &query_type) == -1) { 1481 fprintf(stderr, "Error parsing query: %s\n", query_desc); 1482 return; 1483 } 1484 1485 if (dispatch_query(use_query_id, domain, query_type, 1486 &qpkt, &qpkt_len) == -1) { 1487 char *addrstr; 1488 1489 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen, 1490 serveraddr, sizeof(serveraddr), NULL, 0, 1491 NI_NUMERICHOST) == 0) { 1492 addrstr = serveraddr; 1493 } else 1494 addrstr = "???"; /* XXX: this should not happen */ 1495 fprintf(stderr, "Error sending query to %s: %s\n", 1496 addrstr, query_desc); 1497 return; 1498 } 1499 1500 if (setup_phase == TRUE) { 1501 set_timenow(&time_of_first_query); 1502 time_of_first_query_sec = (double)time_of_first_query.tv_sec + 1503 ((double)time_of_first_query.tv_usec / 1000000.0); 1504 setup_phase = FALSE; 1505 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen, 1506 serveraddr, sizeof(serveraddr), NULL, 0, 1507 NI_NUMERICHOST) != 0) { 1508 fprintf(stderr, "Error printing server address\n"); 1509 return; 1510 } 1511 printf("[Status] Sending queries (beginning with %s)\n", 1512 serveraddr); 1513 } 1514 1515 /* Find the first slot in status[] that is not in use */ 1516 for (count = 0; (status[count].in_use == TRUE) 1517 && (count < max_queries_outstanding); count++); 1518 1519 if (status[count].in_use == TRUE) { 1520 fprintf(stderr, "Unexpected error: We have run out of " 1521 "status[] space!\n"); 1522 return; 1523 } 1524 1525 /* Register the query in status[] */ 1526 status[count].id = use_query_id; 1527 if (verbose) 1528 status[count].desc = strdup(query_desc); 1529 set_timenow(&status[count].sent_timestamp); 1530 status[count].qtype = query_type; 1531 if (dn_expand(qpkt, qpkt + qpkt_len, qpkt + DNS_HEADERLEN, 1532 status[count].qname, MAX_DOMAIN_LEN) == -1) { 1533 fprintf(stderr, "Unexpected error: " 1534 "query message doesn't have qname?\n"); 1535 return; 1536 } 1537 status[count].in_use = TRUE; 1538 1539 if (num_queries_sent_interval == 0) 1540 set_timenow(&time_of_first_query_interval); 1541 1542 num_queries_sent++; 1543 num_queries_sent_interval++; 1544 num_queries_outstanding++; 1545 } 1546 1547 void 1548 register_rtt(struct timeval *timestamp, char *qname, int qtype, 1549 unsigned int rcode) 1550 { 1551 int i; 1552 int oldquery = FALSE; 1553 struct timeval now; 1554 double rtt; 1555 1556 set_timenow(&now); 1557 rtt = difftv(now, *timestamp); 1558 1559 if (difftv(*timestamp, time_of_first_query_interval) < 0) 1560 oldquery = TRUE; 1561 1562 if (rtt_max < 0 || rtt_max < rtt) 1563 rtt_max = rtt; 1564 1565 if (rtt_min < 0 || rtt_min > rtt) 1566 rtt_min = rtt; 1567 1568 rtt_total += rtt; 1569 rtt_counted++; 1570 1571 if (!oldquery) { 1572 if (rtt_max_interval < 0 || rtt_max_interval < rtt) 1573 rtt_max_interval = rtt; 1574 1575 if (rtt_min_interval < 0 || rtt_min_interval > rtt) 1576 rtt_min_interval = rtt; 1577 1578 rtt_total_interval += rtt; 1579 rtt_counted_interval++; 1580 } 1581 1582 if (rttarray == NULL) 1583 return; 1584 1585 i = (int)(rtt * (1000000.0 / rttarray_unit)); 1586 if (i < rttarray_size) { 1587 rttarray[i]++; 1588 if (!oldquery) 1589 rttarray_interval[i]++; 1590 } else { 1591 fprintf(stderr, "Warning: RTT is out of range: %.6lf " 1592 "[query=%s/%d, rcode=%u]\n", rtt, qname, qtype, rcode); 1593 rtt_overflows++; 1594 if (!oldquery) 1595 rtt_overflows_interval++; 1596 } 1597 } 1598 1599 /* 1600 * register_response: 1601 * Register receipt of a query 1602 * 1603 * Removes (sets in_use = FALSE) the record for the given query id in 1604 * status[] if any exists. 1605 */ 1606 void 1607 register_response(unsigned short int id, unsigned int rcode, char *qname, 1608 int qtype) 1609 { 1610 unsigned int ct = 0; 1611 int found = FALSE; 1612 struct timeval now; 1613 double rtt; 1614 1615 if (timeout_queries != NULL) { 1616 struct query_mininfo *qi = &timeout_queries[id]; 1617 1618 if (qi->qtype == qtype && strcasecmp(qi->qname, qname) == 0) { 1619 register_rtt(&qi->sent_timestamp, qname, qtype, rcode); 1620 qi->qtype = -1; 1621 found = TRUE; 1622 } 1623 } 1624 1625 for (; (ct < query_status_allocated) && (found == FALSE); ct++) { 1626 if (status[ct].in_use == TRUE && status[ct].id == id && 1627 status[ct].qtype == qtype && 1628 strcasecmp(status[ct].qname, qname) == 0) { 1629 status[ct].in_use = FALSE; 1630 num_queries_outstanding--; 1631 found = TRUE; 1632 1633 register_rtt(&status[ct].sent_timestamp, qname, qtype, 1634 rcode); 1635 1636 if (status[ct].desc) { 1637 printf("> %s %s\n", rcode_strings[rcode], 1638 status[ct].desc); 1639 free(status[ct].desc); 1640 } 1641 } 1642 } 1643 1644 if (countrcodes && (found == TRUE || target_qps > 0)) 1645 rcodecounts[rcode]++; 1646 1647 if (found == FALSE) { 1648 if (target_qps > 0) { 1649 num_queries_possiblydelayed++; 1650 num_queries_possiblydelayed_interval++; 1651 } else { 1652 fprintf(stderr, 1653 "Warning: Received a response with an " 1654 "unexpected (maybe timed out) id: %u\n", id); 1655 } 1656 } 1657 } 1658 1659 /* 1660 * process_single_response: 1661 * Receive from the given socket & process an invididual response packet. 1662 * Remove it from the list of open queries (status[]) and decrement the 1663 * number of outstanding queries if it matches an open query. 1664 */ 1665 void 1666 process_single_response(int sockfd) { 1667 struct sockaddr_storage from_addr_ss; 1668 struct sockaddr *from_addr; 1669 static unsigned char in_buf[MAX_BUFFER_LEN]; 1670 char qname[MAX_DOMAIN_LEN + 1]; 1671 int numbytes, addr_len, resp_id, qnamelen; 1672 int qtype, flags; 1673 1674 memset(&from_addr_ss, 0, sizeof(from_addr_ss)); 1675 from_addr = (struct sockaddr *)&from_addr_ss; 1676 addr_len = sizeof(from_addr_ss); 1677 1678 if ((numbytes = recvfrom(sockfd, in_buf, MAX_BUFFER_LEN, 1679 0, from_addr, &addr_len)) == -1) { 1680 fprintf(stderr, "Error receiving datagram\n"); 1681 return; 1682 } 1683 1684 if (numbytes < DNS_HEADERLEN) { 1685 if (verbose) 1686 fprintf(stderr, "Malformed response\n"); 1687 return; 1688 } 1689 resp_id = get_uint16(in_buf); 1690 flags = get_uint16(in_buf + 2); 1691 qnamelen = dn_expand(in_buf, in_buf + numbytes, in_buf + DNS_HEADERLEN, 1692 qname, MAX_DOMAIN_LEN); 1693 if (qnamelen == -1) { 1694 if (verbose) 1695 fprintf(stderr, 1696 "Failed to retrieve qname from response\n"); 1697 return; 1698 } 1699 if (numbytes < DNS_HEADERLEN + qnamelen + 2) { 1700 if (verbose) 1701 fprintf(stderr, "Malformed response\n"); 1702 return; 1703 } 1704 qtype = get_uint16(in_buf + DNS_HEADERLEN + qnamelen); 1705 1706 register_response(resp_id, flags & 0xF, qname, qtype); 1707 } 1708 1709 /* 1710 * data_available: 1711 * Is there data available on the given file descriptor? 1712 * 1713 * Return TRUE if there is 1714 * Return FALSE otherwise 1715 */ 1716 int 1717 data_available(double wait) { 1718 fd_set read_fds; 1719 struct timeval tv; 1720 int retval; 1721 int available = FALSE; 1722 int maxfd = -1; 1723 1724 /* Set list of file descriptors */ 1725 FD_ZERO(&read_fds); 1726 if (socket4 != -1) { 1727 FD_SET(socket4, &read_fds); 1728 maxfd = socket4; 1729 } 1730 if (socket6 != -1) { 1731 FD_SET(socket6, &read_fds); 1732 if (maxfd == -1 || maxfd < socket6) 1733 maxfd = socket6; 1734 } 1735 1736 if ((wait > 0.0) && (wait < (double)LONG_MAX)) { 1737 tv.tv_sec = (long)floor(wait); 1738 tv.tv_usec = (long)(1000000.0 * (wait - floor(wait))); 1739 } else { 1740 tv.tv_sec = 0; 1741 tv.tv_usec = 0; 1742 } 1743 1744 retval = select(maxfd + 1, &read_fds, NULL, NULL, &tv); 1745 1746 if (socket4 != -1 && FD_ISSET(socket4, &read_fds)) { 1747 available = TRUE; 1748 process_single_response(socket4); 1749 } 1750 if (socket6 != -1 && FD_ISSET(socket6, &read_fds)) { 1751 available = TRUE; 1752 process_single_response(socket6); 1753 } 1754 1755 return (available); 1756 } 1757 1758 /* 1759 * process_responses: 1760 * Go through any/all received responses and remove them from the list of 1761 * open queries (set in_use = FALSE for their entry in status[]), also 1762 * decrementing the number of outstanding queries. 1763 */ 1764 void 1765 process_responses(int adjust_rate) { 1766 double wait; 1767 struct timeval now, waituntil; 1768 double first_packet_wait = RESPONSE_BLOCKING_WAIT_TIME; 1769 unsigned int outstanding = queries_outstanding(); 1770 1771 if (adjust_rate == TRUE) { 1772 double u; 1773 1774 u = time_of_first_query_sec + 1775 query_interval * num_queries_sent; 1776 waituntil.tv_sec = (long)floor(u); 1777 waituntil.tv_usec = (long)(1000000.0 * (u - waituntil.tv_sec)); 1778 1779 /* 1780 * Wait until a response arrives or the specified limit is 1781 * reached. 1782 */ 1783 while (1) { 1784 set_timenow(&now); 1785 wait = difftv(waituntil, now); 1786 if (wait <= 0) 1787 wait = 0.0; 1788 if (data_available(wait) != TRUE) 1789 break; 1790 1791 /* 1792 * We have reached the limit. Read as many responses 1793 * as possible without waiting, and exit. 1794 */ 1795 if (wait == 0) { 1796 while (data_available(0.0) == TRUE) 1797 ; 1798 break; 1799 } 1800 } 1801 } else { 1802 /* 1803 * Don't block waiting for packets at all if we aren't 1804 * looking for any responses or if we are now able to send new 1805 * queries. 1806 */ 1807 if ((outstanding == 0) || 1808 (outstanding < max_queries_outstanding)) { 1809 first_packet_wait = 0.0; 1810 } 1811 1812 if (data_available(first_packet_wait) == TRUE) { 1813 while (data_available(0.0) == TRUE) 1814 ; 1815 } 1816 } 1817 } 1818 1819 /* 1820 * retire_old_queries: 1821 * Go through the list of open queries (status[]) and remove any queries 1822 * (i.e. set in_use = FALSE) which are older than the timeout, decrementing 1823 * the number of queries outstanding for each one removed. 1824 */ 1825 void 1826 retire_old_queries(int sending) { 1827 unsigned int count = 0; 1828 struct timeval curr_time; 1829 double timeout = query_timeout; 1830 int timeout_reduced = FALSE; 1831 1832 /* 1833 * If we have target qps and would not be able to send any packets 1834 * due to buffer full, check whether we are behind the schedule. 1835 * If we are, purge some queries more aggressively. 1836 */ 1837 if (target_qps > 0 && sending == TRUE && count == 0 && 1838 queries_outstanding() == max_queries_outstanding) { 1839 struct timeval next, now; 1840 double n; 1841 1842 n = time_of_first_query_sec + 1843 query_interval * num_queries_sent; 1844 next.tv_sec = (long)floor(n); 1845 next.tv_usec = (long)(1000000.0 * (n - next.tv_sec)); 1846 1847 set_timenow(&now); 1848 if (difftv(next, now) <= 0) { 1849 timeout_reduced = TRUE; 1850 timeout = 0.001; /* XXX: ad-hoc value */ 1851 } 1852 } 1853 1854 set_timenow(&curr_time); 1855 1856 for (; count < query_status_allocated; count++) { 1857 if ((status[count].in_use == TRUE) && 1858 (difftv(curr_time, 1859 status[count].sent_timestamp) >= (double)timeout)) 1860 { 1861 status[count].in_use = FALSE; 1862 num_queries_outstanding--; 1863 1864 if (timeout_queries != NULL) { 1865 struct query_mininfo *qi; 1866 1867 qi = &timeout_queries[status[count].id]; 1868 if (qi->qtype != -1) { 1869 /* now really retire this query */ 1870 num_queries_timed_out++; 1871 num_queries_timed_out_interval++; 1872 } 1873 qi->qtype = status[count].qtype; 1874 qi->sent_timestamp = 1875 status[count].sent_timestamp; 1876 strcpy(qi->qname, status[count].qname); 1877 } else { 1878 num_queries_timed_out++; 1879 num_queries_timed_out_interval++; 1880 } 1881 1882 if (timeout_reduced == FALSE) { 1883 if (status[count].desc) { 1884 printf("> T %s\n", status[count].desc); 1885 free(status[count].desc); 1886 } else { 1887 printf("[Timeout] Query timed out: " 1888 "msg id %u\n", 1889 status[count].id); 1890 } 1891 } 1892 } 1893 } 1894 } 1895 1896 /* 1897 * print_histogram 1898 * Print RTT histogram to the specified file in the gnuplot format 1899 */ 1900 void 1901 print_histogram(unsigned int total) { 1902 int i; 1903 double ratio; 1904 FILE *fp; 1905 1906 if (rtt_histogram_file == NULL || rttarray == NULL) 1907 return; 1908 1909 fp = fopen((const char *)rtt_histogram_file, "w+"); 1910 if (fp == NULL) { 1911 fprintf(stderr, "Error opening RTT histogram file: %s\n", 1912 rtt_histogram_file); 1913 return; 1914 } 1915 1916 for (i = 0; i < rttarray_size; i++) { 1917 ratio = ((double)rttarray[i] / (double)total) * 100; 1918 fprintf(fp, "%.6lf %.3lf\n", 1919 (double)(i * rttarray_unit) + 1920 (double)rttarray_unit / 2, 1921 ratio); 1922 } 1923 1924 (void)fclose(fp); 1925 } 1926 1927 /* 1928 * print_statistics: 1929 * Print out statistics based on the results of the test 1930 */ 1931 void 1932 print_statistics(int intermediate, unsigned int sent, unsigned int timed_out, 1933 unsigned int possibly_delayed, 1934 struct timeval *first_query, 1935 struct timeval *program_start, 1936 struct timeval *end_perf, struct timeval *end_query, 1937 unsigned int rcounted, double rmax, double rmin, double rtotal, 1938 unsigned int roverflows, unsigned int *rarray) 1939 { 1940 unsigned int num_queries_completed; 1941 double per_lost, per_completed, per_lost2, per_completed2; 1942 double run_time, queries_per_sec, queries_per_sec2; 1943 double queries_per_sec_total; 1944 double rtt_average, rtt_stddev; 1945 struct timeval start_time; 1946 1947 num_queries_completed = sent - timed_out; 1948 1949 if (num_queries_completed == 0) { 1950 per_lost = 0.0; 1951 per_completed = 0.0; 1952 1953 per_lost2 = 0.0; 1954 per_completed2 = 0.0; 1955 } else { 1956 per_lost = (100.0 * (double)timed_out) / (double)sent; 1957 per_completed = 100.0 - per_lost; 1958 1959 per_lost2 = (100.0 * (double)(timed_out - possibly_delayed)) 1960 / (double)sent; 1961 per_completed2 = 100 - per_lost2; 1962 } 1963 1964 if (sent == 0) { 1965 start_time.tv_sec = program_start->tv_sec; 1966 start_time.tv_usec = program_start->tv_usec; 1967 run_time = 0.0; 1968 queries_per_sec = 0.0; 1969 queries_per_sec2 = 0.0; 1970 queries_per_sec_total = 0.0; 1971 } else { 1972 start_time.tv_sec = first_query->tv_sec; 1973 start_time.tv_usec = first_query->tv_usec; 1974 run_time = difftv(*end_perf, *first_query); 1975 queries_per_sec = (double)num_queries_completed / run_time; 1976 queries_per_sec2 = (double)(num_queries_completed + 1977 possibly_delayed) / run_time; 1978 1979 queries_per_sec_total = (double)sent / 1980 difftv(*end_query, *first_query); 1981 } 1982 1983 if (rcounted > 0) { 1984 int i; 1985 double sum = 0; 1986 1987 rtt_average = rtotal / (double)rcounted; 1988 for (i = 0; i < rttarray_size; i++) { 1989 if (rarray[i] != 0) { 1990 double mean, diff; 1991 1992 mean = (double)(i * rttarray_unit) + 1993 (double)rttarray_unit / 2; 1994 diff = rtt_average - (mean / 1000000.0); 1995 sum += (diff * diff) * rarray[i]; 1996 } 1997 } 1998 rtt_stddev = sqrt(sum / (double)rcounted); 1999 } else { 2000 rtt_average = 0.0; 2001 rtt_stddev = 0.0; 2002 } 2003 2004 printf("\n"); 2005 2006 printf("%sStatistics:\n", intermediate ? "Intermediate " : ""); 2007 2008 printf("\n"); 2009 2010 if (!intermediate) { 2011 printf(" Parse input file: %s\n", 2012 ((run_only_once == TRUE) ? "once" : "multiple times")); 2013 if (use_timelimit) 2014 printf(" Run time limit: %u seconds\n", 2015 run_timelimit); 2016 if (run_only_once == FALSE) 2017 printf(" Ran through file: %u times\n", 2018 runs_through_file); 2019 else 2020 printf(" Ended due to: reaching %s\n", 2021 ((runs_through_file == 0) ? "time limit" 2022 : "end of file")); 2023 2024 printf("\n"); 2025 } 2026 2027 printf(" Queries sent: %u queries\n", sent); 2028 printf(" Queries completed: %u queries\n", num_queries_completed); 2029 printf(" Queries lost: %u queries\n", timed_out); 2030 printf(" Queries delayed(?): %u queries\n", possibly_delayed); 2031 2032 printf("\n"); 2033 2034 printf(" RTT max: %3.6lf sec\n", rmax); 2035 printf(" RTT min: %3.6lf sec\n", rmin); 2036 printf(" RTT average: %3.6lf sec\n", rtt_average); 2037 printf(" RTT std deviation: %3.6lf sec\n", rtt_stddev); 2038 printf(" RTT out of range: %u queries\n", roverflows); 2039 2040 if (!intermediate) /* XXX should we print this case also? */ 2041 print_histogram(num_queries_completed); 2042 2043 printf("\n"); 2044 2045 if (countrcodes) { 2046 unsigned int i; 2047 2048 for (i = 0; i < 16; i++) { 2049 if (rcodecounts[i] == 0) 2050 continue; 2051 printf(" Returned %8s: %u queries\n", 2052 rcode_strings[i], rcodecounts[i]); 2053 } 2054 printf("\n"); 2055 } 2056 2057 printf(" Percentage completed: %6.2lf%%\n", per_completed); 2058 if (possibly_delayed > 0) 2059 printf(" (w/ delayed qrys): %6.2lf%%\n", per_completed2); 2060 printf(" Percentage lost: %6.2lf%%\n", per_lost); 2061 if (possibly_delayed > 0) 2062 printf(" (w/o delayed qrys): %6.2lf%%\n", per_lost2); 2063 2064 printf("\n"); 2065 2066 printf(" Started at: %s", 2067 ctime((const time_t *)&start_time.tv_sec)); 2068 printf(" Finished at: %s", 2069 ctime((const time_t *)&end_perf->tv_sec)); 2070 printf(" Ran for: %.6lf seconds\n", run_time); 2071 2072 printf("\n"); 2073 2074 printf(" Queries per second: %.6lf qps\n", queries_per_sec); 2075 if (possibly_delayed > 0) { 2076 printf(" (w/ delayed qrys): %.6lf qps\n", 2077 queries_per_sec2); 2078 } 2079 if (target_qps > 0) { 2080 printf(" Total QPS/target: %.6lf/%d qps\n", 2081 queries_per_sec_total, target_qps); 2082 } 2083 2084 printf("\n"); 2085 } 2086 2087 void 2088 print_interval_statistics() { 2089 struct timeval time_now; 2090 2091 if (use_timelimit == FALSE) 2092 return; 2093 2094 if (setup_phase == TRUE) 2095 return; 2096 2097 if (print_interval == 0) 2098 return; 2099 2100 if (timelimit_reached() == TRUE) 2101 return; 2102 2103 set_timenow(&time_now); 2104 if (difftv(time_now, time_of_first_query_interval) 2105 <= (double)print_interval) 2106 return; 2107 2108 /* Don't count currently outstanding queries */ 2109 num_queries_sent_interval -= queries_outstanding(); 2110 print_statistics(TRUE, num_queries_sent_interval, 2111 num_queries_timed_out_interval, 2112 num_queries_possiblydelayed_interval, 2113 &time_of_first_query_interval, 2114 &time_of_first_query_interval, &time_now, &time_now, 2115 rtt_counted_interval, rtt_max_interval, 2116 rtt_min_interval, rtt_total_interval, 2117 rtt_overflows_interval, rttarray_interval); 2118 2119 /* Reset intermediate counters */ 2120 num_queries_sent_interval = 0; 2121 num_queries_timed_out_interval = 0; 2122 num_queries_possiblydelayed_interval = 0; 2123 rtt_max_interval = -1; 2124 rtt_min_interval = -1; 2125 rtt_total_interval = 0.0; 2126 rtt_counted_interval = 0.0; 2127 rtt_overflows_interval = 0; 2128 if (rttarray_interval != NULL) { 2129 memset(rttarray_interval, 0, 2130 sizeof(rttarray_interval[0]) * rttarray_size); 2131 } 2132 } 2133 2134 /* 2135 * queryperf Program Mainline 2136 */ 2137 int 2138 main(int argc, char **argv) { 2139 int adjust_rate; 2140 int sending = FALSE; 2141 int got_eof = FALSE; 2142 int input_length = MAX_INPUT_LEN; 2143 char input_line[MAX_INPUT_LEN + 1]; 2144 2145 set_timenow(&time_of_program_start); 2146 time_of_first_query.tv_sec = 0; 2147 time_of_first_query.tv_usec = 0; 2148 time_of_first_query_interval.tv_sec = 0; 2149 time_of_first_query_interval.tv_usec = 0; 2150 time_of_end_of_run.tv_sec = 0; 2151 time_of_end_of_run.tv_usec = 0; 2152 2153 input_line[0] = '\0'; 2154 2155 show_startup_info(); 2156 2157 if (setup(argc, argv) == -1) 2158 return (-1); 2159 2160 /* XXX: move this to setup: */ 2161 timeout_queries = malloc(sizeof(struct query_mininfo) * 65536); 2162 if (timeout_queries == NULL) { 2163 fprintf(stderr, 2164 "failed to allocate memory for timeout queries\n"); 2165 return (-1); 2166 } else { 2167 int i; 2168 for (i = 0; i < 65536; i++) 2169 timeout_queries[i].qtype = -1; 2170 } 2171 2172 printf("[Status] Processing input data\n"); 2173 2174 while ((sending = keep_sending(&got_eof)) == TRUE || 2175 queries_outstanding() > 0) 2176 { 2177 if (num_queries_sent_interval > 0){ 2178 /* 2179 * After statistics are printed, send_query() 2180 * needs to be called at least once so that 2181 * time_of_first_query_interval is reset 2182 */ 2183 print_interval_statistics(); 2184 } 2185 adjust_rate = FALSE; 2186 2187 while ((sending = keep_sending(&got_eof)) == TRUE && 2188 queries_outstanding() < max_queries_outstanding) 2189 { 2190 int len = next_input_line(input_line, input_length); 2191 if (len == 0) { 2192 got_eof = TRUE; 2193 } else { 2194 /* Zap the trailing newline */ 2195 if (input_line[len - 1] == '\n') 2196 input_line[len - 1] = '\0'; 2197 2198 /* 2199 * TODO: Should test if we got a whole line 2200 * and flush to the next \n in input if not 2201 * here... Add this later. Only do the next 2202 * few lines if we got a whole line, else 2203 * print a warning. Alternative: Make the 2204 * max line size really big. BAD! :) 2205 */ 2206 2207 if (input_line[0] == CONFIG_CHAR) 2208 update_config(input_line); 2209 else { 2210 send_query(input_line); 2211 if (target_qps > 0 && 2212 (num_queries_sent % 2213 max_queries_outstanding) == 0) { 2214 adjust_rate = TRUE; 2215 } 2216 } 2217 } 2218 } 2219 2220 process_responses(adjust_rate); 2221 retire_old_queries(sending); 2222 } 2223 2224 set_timenow(&time_of_end_of_run); 2225 2226 printf("[Status] Testing complete\n"); 2227 2228 close_socket(); 2229 close_datafile(); 2230 2231 print_statistics(FALSE, num_queries_sent, num_queries_timed_out, 2232 num_queries_possiblydelayed, 2233 &time_of_first_query, &time_of_program_start, 2234 &time_of_end_of_run, &time_of_stop_sending, 2235 rtt_counted, rtt_max, rtt_min, rtt_total, 2236 rtt_overflows, rttarray); 2237 2238 return (0); 2239 } 2240