No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / queryperf / queryperf.c
blobaa8824fc879e5ed0bf2a9140e08a2a738374a533
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2000, 2001 Nominum, Inc.
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.
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.
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 ***/
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <time.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
40 #include <resolv.h>
41 #include <math.h>
42 #include <errno.h>
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #ifndef HAVE_GETADDRINFO
47 #include "missing/addrinfo.h"
48 #endif
49 #endif
52 * Configuration defaults
55 #define DEF_MAX_QUERIES_OUTSTANDING 20
56 #define DEF_QUERY_TIMEOUT 5 /* in seconds */
57 #define DEF_SERVER_TO_QUERY "127.0.0.1"
58 #define DEF_SERVER_PORT "53"
59 #define DEF_BUFFER_SIZE 32 /* in k */
61 #define DEF_RTTARRAY_SIZE 50000
62 #define DEF_RTTARRAY_UNIT 100 /* in usec */
65 * Other constants / definitions
68 #define COMMENT_CHAR ';'
69 #define CONFIG_CHAR '#'
70 #define MAX_PORT 65535
71 #define MAX_INPUT_LEN 512
72 #define MAX_DOMAIN_LEN 255
73 #define MAX_BUFFER_LEN 8192 /* in bytes */
74 #define HARD_TIMEOUT_EXTRA 5 /* in seconds */
75 #define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */
76 #define EDNSLEN 11
78 #define FALSE 0
79 #define TRUE 1
81 #define WHITESPACE " \t\n"
83 enum directives_enum { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT };
84 #define DIRECTIVES { "server", "port", "maxqueries", "maxwait" }
85 #define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }
87 #define QTYPE_STRINGS { \
88 "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", \
89 "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", \
90 "AAAA", "SRV", "NAPTR", "A6", "AXFR", "MAILB", "MAILA", "*", "ANY" \
93 #define QTYPE_CODES { \
94 1, 2, 3, 4, 5, 6, 7, 8, \
95 9, 10, 11, 12, 13, 14, 15, 16, \
96 28, 33, 35, 38, 252, 253, 254, 255, 255 \
99 #define RCODE_STRINGS { \
100 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
101 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
102 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
103 "rcode12", "rcode13", "rcode14", "rcode15" \
107 * Data type definitions
110 #define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */
111 #define VALID_QUERY_STATUS(q) ((q) != NULL && \
112 (q)->magic == QUERY_STATUS_MAGIC)
114 struct query_status {
115 unsigned int magic;
116 int in_use;
117 unsigned short int id;
118 struct timeval sent_timestamp;
119 char *desc;
123 * Configuration options (global)
126 unsigned int max_queries_outstanding; /* init 0 */
127 unsigned int query_timeout = DEF_QUERY_TIMEOUT;
128 int ignore_config_changes = FALSE;
129 unsigned int socket_bufsize = DEF_BUFFER_SIZE;
131 int family = AF_UNSPEC;
132 int use_stdin = TRUE;
133 char *datafile_name; /* init NULL */
135 char *server_to_query; /* init NULL */
136 char *server_port; /* init NULL */
137 struct addrinfo *server_ai; /* init NULL */
139 int run_only_once = FALSE;
140 int use_timelimit = FALSE;
141 unsigned int run_timelimit; /* init 0 */
142 unsigned int print_interval; /* init 0 */
144 unsigned int target_qps; /* init 0 */
146 int serverset = FALSE, portset = FALSE;
147 int queriesset = FALSE, timeoutset = FALSE;
148 int edns = FALSE, dnssec = FALSE;
149 int countrcodes = FALSE;
150 int rcodecounts[16] = {0};
152 int verbose = FALSE;
155 * Other global stuff
158 int setup_phase = TRUE;
160 FILE *datafile_ptr; /* init NULL */
161 unsigned int runs_through_file; /* init 0 */
163 unsigned int num_queries_sent; /* init 0 */
164 unsigned int num_queries_sent_interval;
165 unsigned int num_queries_outstanding; /* init 0 */
166 unsigned int num_queries_timed_out; /* init 0 */
167 unsigned int num_queries_possiblydelayed; /* init 0 */
168 unsigned int num_queries_timed_out_interval;
169 unsigned int num_queries_possiblydelayed_interval;
171 struct timeval time_of_program_start;
172 struct timeval time_of_first_query;
173 double time_of_first_query_sec;
174 struct timeval time_of_first_query_interval;
175 struct timeval time_of_end_of_run;
176 struct timeval time_of_stop_sending;
178 struct timeval time_of_queryset_start;
179 double query_interval;
180 struct timeval time_of_next_queryset;
182 double rtt_max = -1;
183 double rtt_max_interval = -1;
184 double rtt_min = -1;
185 double rtt_min_interval = -1;
186 double rtt_total;
187 double rtt_total_interval;
188 int rttarray_size = DEF_RTTARRAY_SIZE;
189 int rttarray_unit = DEF_RTTARRAY_UNIT;
190 unsigned int *rttarray = NULL;
191 unsigned int *rttarray_interval = NULL;
192 unsigned int rtt_overflows;
193 unsigned int rtt_overflows_interval;
194 char *rtt_histogram_file = NULL;
196 struct query_status *status; /* init NULL */
197 unsigned int query_status_allocated; /* init 0 */
199 int query_socket = -1;
200 int socket4 = -1, socket6 = -1;
202 static char *rcode_strings[] = RCODE_STRINGS;
205 * get_uint16:
206 * Get an unsigned short integer from a buffer (in network order)
208 static unsigned short
209 get_uint16(unsigned char *buf) {
210 unsigned short ret;
212 ret = buf[0] * 256 + buf[1];
214 return (ret);
218 * show_startup_info:
219 * Show name/version
221 void
222 show_startup_info(void) {
223 printf("\n"
224 "DNS Query Performance Testing Tool\n"
225 "Version: Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp\n"
226 "\n");
230 * show_usage:
231 * Print out usage/syntax information
233 void
234 show_usage(void) {
235 fprintf(stderr,
236 "\n"
237 "Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n"
238 " [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n"
239 " [-i interval] [-r arraysize] [-u unit] [-H histfile]\n"
240 " [-T qps] [-e] [-D] [-c] [-v] [-h]\n"
241 " -d specifies the input data file (default: stdin)\n"
242 " -s sets the server to query (default: %s)\n"
243 " -p sets the port on which to query the server (default: %s)\n"
244 " -q specifies the maximum number of queries outstanding (default: %d)\n"
245 " -t specifies the timeout for query completion in seconds (default: %d)\n"
246 " -n causes configuration changes to be ignored\n"
247 " -l specifies how a limit for how long to run tests in seconds (no default)\n"
248 " -1 run through input only once (default: multiple iff limit given)\n"
249 " -b set input/output buffer size in kilobytes (default: %d k)\n"
250 " -i specifies interval of intermediate outputs in seconds (default: 0=none)\n"
251 " -f specify address family of DNS transport, inet or inet6 (default: any)\n"
252 " -r set RTT statistics array size (default: %d)\n"
253 " -u set RTT statistics time unit in usec (default: %d)\n"
254 " -H specifies RTT histogram data file (default: none)\n"
255 " -T specify the target qps (default: 0=unspecified)\n"
256 " -e enable EDNS 0\n"
257 " -D set the DNSSEC OK bit (implies EDNS)\n"
258 " -c print the number of packets with each rcode\n"
259 " -v verbose: report the RCODE of each response on stdout\n"
260 " -h print this usage\n"
261 "\n",
262 DEF_SERVER_TO_QUERY, DEF_SERVER_PORT,
263 DEF_MAX_QUERIES_OUTSTANDING, DEF_QUERY_TIMEOUT,
264 DEF_BUFFER_SIZE, DEF_RTTARRAY_SIZE, DEF_RTTARRAY_UNIT);
268 * set_datafile:
269 * Set the datafile to read
271 * Return -1 on failure
272 * Return a non-negative integer otherwise
275 set_datafile(char *new_file) {
276 char *dfname_tmp;
278 if ((new_file == NULL) || (new_file[0] == '\0')) {
279 fprintf(stderr, "Error: null datafile name\n");
280 return (-1);
283 if ((dfname_tmp = malloc(strlen(new_file) + 1)) == NULL) {
284 fprintf(stderr, "Error allocating memory for datafile name: "
285 "%s\n", new_file);
286 return (-1);
289 free(datafile_name);
290 datafile_name = dfname_tmp;
292 strcpy(datafile_name, new_file);
293 use_stdin = FALSE;
295 return (0);
299 * set_input_stdin:
300 * Set the input to be stdin (instead of a datafile)
302 void
303 set_input_stdin(void) {
304 use_stdin = TRUE;
305 free(datafile_name);
306 datafile_name = NULL;
310 * set_server:
311 * Set the server to be queried
313 * Return -1 on failure
314 * Return a non-negative integer otherwise
317 set_server(char *new_name) {
318 static struct hostent *server_he;
320 /* If no change in server name, don't do anything... */
321 if ((server_to_query != NULL) && (new_name != NULL))
322 if (strcmp(new_name, server_to_query) == 0)
323 return (0);
325 if ((new_name == NULL) || (new_name[0] == '\0')) {
326 fprintf(stderr, "Error: null server name\n");
327 return (-1);
330 free(server_to_query);
331 server_to_query = NULL;
333 if ((server_to_query = malloc(strlen(new_name) + 1)) == NULL) {
334 fprintf(stderr, "Error allocating memory for server name: "
335 "%s\n", new_name);
336 return (-1);
339 strcpy(server_to_query, new_name);
341 return (0);
345 * set_server_port:
346 * Set the port on which to contact the server
348 * Return -1 if port is invalid
349 * Return a non-negative integer otherwise
352 set_server_port(char *new_port) {
353 unsigned int uint_val;
355 if ((is_uint(new_port, &uint_val)) != TRUE)
356 return (-1);
358 if (uint_val && uint_val > MAX_PORT)
359 return (-1);
360 else {
361 if (server_port != NULL && new_port != NULL &&
362 strcmp(server_port, new_port) == 0)
363 return (0);
365 free(server_port);
366 server_port = NULL;
368 if ((server_port = malloc(strlen(new_port) + 1)) == NULL) {
369 fprintf(stderr,
370 "Error allocating memory for server port: "
371 "%s\n", new_port);
372 return (-1);
375 strcpy(server_port, new_port);
377 return (0);
382 set_server_sa(void) {
383 struct addrinfo hints, *res;
384 static struct protoent *proto;
385 int error;
387 if (proto == NULL && (proto = getprotobyname("udp")) == NULL) {
388 fprintf(stderr, "Error: getprotobyname call failed");
389 return (-1);
392 memset(&hints, 0, sizeof(hints));
393 hints.ai_family = family;
394 hints.ai_socktype = SOCK_DGRAM;
395 hints.ai_protocol = proto->p_proto;
396 if ((error = getaddrinfo(server_to_query, server_port,
397 &hints, &res)) != 0) {
398 fprintf(stderr, "Error: getaddrinfo(%s, %s) failed\n",
399 server_to_query, server_port);
400 return (-1);
403 /* replace the server's addrinfo */
404 if (server_ai != NULL)
405 freeaddrinfo(server_ai);
406 server_ai = res;
407 return (0);
411 * is_digit:
412 * Tests if a character is a digit
414 * Return TRUE if it is
415 * Return FALSE if it is not
418 is_digit(char d) {
419 if (d < '0' || d > '9')
420 return (FALSE);
421 else
422 return (TRUE);
426 * is_uint:
427 * Tests if a string, test_int, is a valid unsigned integer
429 * Sets *result to be the unsigned integer if it is valid
431 * Return TRUE if it is
432 * Return FALSE if it is not
435 is_uint(char *test_int, unsigned int *result) {
436 unsigned long int value;
437 char *end;
439 if (test_int == NULL)
440 return (FALSE);
442 if (is_digit(test_int[0]) == FALSE)
443 return (FALSE);
445 value = strtoul(test_int, &end, 10);
447 if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX))
448 return (FALSE);
450 *result = (unsigned int)value;
451 return (TRUE);
455 * set_max_queries:
456 * Set the maximum number of outstanding queries
458 * Returns -1 on failure
459 * Returns a non-negative integer otherwise
462 set_max_queries(unsigned int new_max) {
463 static unsigned int size_qs = sizeof(struct query_status);
464 struct query_status *temp_stat;
465 unsigned int count;
467 if (new_max < 0) {
468 fprintf(stderr, "Unable to change max outstanding queries: "
469 "must be positive and non-zero: %u\n", new_max);
470 return (-1);
473 if (new_max > query_status_allocated) {
474 temp_stat = realloc(status, new_max * size_qs);
476 if (temp_stat == NULL) {
477 fprintf(stderr, "Error resizing query_status\n");
478 return (-1);
479 } else {
480 status = temp_stat;
483 * Be careful to only initialise between above
484 * the previously allocated space. Note that the
485 * allocation may be larger than the current
486 * max_queries_outstanding. We don't want to
487 * "forget" any outstanding queries! We might
488 * still have some above the bounds of the max.
490 count = query_status_allocated;
491 for (; count < new_max; count++) {
492 status[count].in_use = FALSE;
493 status[count].magic = QUERY_STATUS_MAGIC;
494 status[count].desc = NULL;
497 query_status_allocated = new_max;
501 max_queries_outstanding = new_max;
503 return (0);
507 * parse_args:
508 * Parse program arguments and set configuration options
510 * Return -1 on failure
511 * Return a non-negative integer otherwise
514 parse_args(int argc, char **argv) {
515 int c;
516 unsigned int uint_arg_val;
518 while ((c = getopt(argc, argv,
519 "f:q:t:i:nd:s:p:1l:b:eDcvr:T::u:H:h")) != -1) {
520 switch (c) {
521 case 'f':
522 if (strcmp(optarg, "inet") == 0)
523 family = AF_INET;
524 #ifdef AF_INET6
525 else if (strcmp(optarg, "inet6") == 0)
526 family = AF_INET6;
527 #endif
528 else if (strcmp(optarg, "any") == 0)
529 family = AF_UNSPEC;
530 else {
531 fprintf(stderr, "Invalid address family: %s\n",
532 optarg);
533 return (-1);
535 break;
536 case 'q':
537 if (is_uint(optarg, &uint_arg_val) == TRUE) {
538 set_max_queries(uint_arg_val);
539 queriesset = TRUE;
540 } else {
541 fprintf(stderr, "Option requires a positive "
542 "integer value: -%c %s\n",
543 c, optarg);
544 return (-1);
546 break;
548 case 't':
549 if (is_uint(optarg, &uint_arg_val) == TRUE) {
550 query_timeout = uint_arg_val;
551 timeoutset = TRUE;
552 } else {
553 fprintf(stderr, "Option requires a positive "
554 "integer value: -%c %s\n",
555 c, optarg);
556 return (-1);
558 break;
560 case 'n':
561 ignore_config_changes = TRUE;
562 break;
564 case 'd':
565 if (set_datafile(optarg) == -1) {
566 fprintf(stderr, "Error setting datafile "
567 "name: %s\n", optarg);
568 return (-1);
570 break;
572 case 's':
573 if (set_server(optarg) == -1) {
574 fprintf(stderr, "Error setting server "
575 "name: %s\n", optarg);
576 return (-1);
578 serverset = TRUE;
579 break;
581 case 'p':
582 if (is_uint(optarg, &uint_arg_val) == TRUE &&
583 uint_arg_val < MAX_PORT)
585 set_server_port(optarg);
586 portset = TRUE;
587 } else {
588 fprintf(stderr, "Option requires a positive "
589 "integer between 0 and %d: -%c %s\n",
590 MAX_PORT - 1, c, optarg);
591 return (-1);
593 break;
595 case '1':
596 run_only_once = TRUE;
597 break;
599 case 'l':
600 if (is_uint(optarg, &uint_arg_val) == TRUE) {
601 use_timelimit = TRUE;
602 run_timelimit = uint_arg_val;
603 } else {
604 fprintf(stderr, "Option requires a positive "
605 "integer: -%c %s\n",
606 c, optarg);
607 return (-1);
609 break;
611 case 'b':
612 if (is_uint(optarg, &uint_arg_val) == TRUE) {
613 socket_bufsize = uint_arg_val;
614 } else {
615 fprintf(stderr, "Option requires a positive "
616 "integer: -%c %s\n",
617 c, optarg);
618 return (-1);
620 break;
621 case 'e':
622 edns = TRUE;
623 break;
624 case 'D':
625 dnssec = TRUE;
626 edns = TRUE;
627 break;
628 case 'c':
629 countrcodes = TRUE;
630 break;
631 case 'v':
632 verbose = 1;
633 break;
634 case 'i':
635 if (is_uint(optarg, &uint_arg_val) == TRUE)
636 print_interval = uint_arg_val;
637 else {
638 fprintf(stderr, "Invalid interval: %s\n",
639 optarg);
640 return (-1);
642 break;
643 case 'r':
644 if (is_uint(optarg, &uint_arg_val) == TRUE)
645 rttarray_size = uint_arg_val;
646 else {
647 fprintf(stderr, "Invalid RTT array size: %s\n",
648 optarg);
649 return (-1);
651 break;
652 case 'u':
653 if (is_uint(optarg, &uint_arg_val) == TRUE)
654 rttarray_unit = uint_arg_val;
655 else {
656 fprintf(stderr, "Invalid RTT unit: %s\n",
657 optarg);
658 return (-1);
660 break;
661 case 'H':
662 rtt_histogram_file = optarg;
663 break;
664 case 'T':
665 if (is_uint(optarg, &uint_arg_val) == TRUE)
666 target_qps = uint_arg_val;
667 else {
668 fprintf(stderr, "Invalid target qps: %s\n",
669 optarg);
670 return (-1);
672 break;
673 case 'h':
674 return (-1);
675 default:
676 fprintf(stderr, "Invalid option: -%c\n", optopt);
677 return (-1);
681 if (run_only_once == FALSE && use_timelimit == FALSE)
682 run_only_once = TRUE;
684 return (0);
688 * open_datafile:
689 * Open the data file ready for reading
691 * Return -1 on failure
692 * Return non-negative integer on success
695 open_datafile(void) {
696 if (use_stdin == TRUE) {
697 datafile_ptr = stdin;
698 return (0);
699 } else {
700 if ((datafile_ptr = fopen(datafile_name, "r")) == NULL) {
701 fprintf(stderr, "Error: unable to open datafile: %s\n",
702 datafile_name);
703 return (-1);
704 } else
705 return (0);
710 * close_datafile:
711 * Close the data file if any is open
713 * Return -1 on failure
714 * Return non-negative integer on success, including if not needed
717 close_datafile(void) {
718 if ((use_stdin == FALSE) && (datafile_ptr != NULL)) {
719 if (fclose(datafile_ptr) != 0) {
720 fprintf(stderr, "Error: unable to close datafile\n");
721 return (-1);
725 return (0);
729 * open_socket:
730 * Open a socket for the queries. When we have an active socket already,
731 * close it and open a new one.
733 * Return -1 on failure
734 * Return the socket identifier
737 open_socket(void) {
738 int sock;
739 int ret;
740 int bufsize;
741 struct addrinfo hints, *res;
743 memset(&hints, 0, sizeof(hints));
744 hints.ai_family = server_ai->ai_family;
745 hints.ai_socktype = server_ai->ai_socktype;
746 hints.ai_protocol = server_ai->ai_protocol;
747 hints.ai_flags = AI_PASSIVE;
749 if ((ret = getaddrinfo(NULL, "0", &hints, &res)) != 0) {
750 fprintf(stderr,
751 "Error: getaddrinfo for bind socket failed: %s\n",
752 gai_strerror(ret));
753 return (-1);
756 if ((sock = socket(res->ai_family, SOCK_DGRAM,
757 res->ai_protocol)) == -1) {
758 fprintf(stderr, "Error: socket call failed");
759 goto fail;
762 #if defined(AF_INET6) && defined(IPV6_V6ONLY)
763 if (res->ai_family == AF_INET6) {
764 int on = 1;
766 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
767 &on, sizeof(on)) == -1) {
768 fprintf(stderr,
769 "Warning: setsockopt(IPV6_V6ONLY) failed\n");
772 #endif
774 if (bind(sock, res->ai_addr, res->ai_addrlen) == -1)
775 fprintf(stderr, "Error: bind call failed");
777 freeaddrinfo(res);
779 bufsize = 1024 * socket_bufsize;
781 ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
782 (char *) &bufsize, sizeof(bufsize));
783 if (ret < 0)
784 fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n");
786 ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
787 (char *) &bufsize, sizeof(bufsize));
788 if (ret < 0)
789 fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n");
791 return (sock);
793 fail:
794 if (res)
795 freeaddrinfo(res);
796 return (-1);
800 * close_socket:
801 * Close the query socket(s)
803 * Return -1 on failure
804 * Return a non-negative integer otherwise
807 close_socket(void) {
808 if (socket4 != -1) {
809 if (close(socket4) != 0) {
810 fprintf(stderr,
811 "Error: unable to close IPv4 socket\n");
812 return (-1);
816 if (socket6 != -1) {
817 if (close(socket6) != 0) {
818 fprintf(stderr,
819 "Error: unable to close IPv6 socket\n");
820 return (-1);
824 query_socket = -1;
826 return (0);
830 * change_socket:
831 * Choose an appropriate socket according to the address family of the
832 * current server. Open a new socket if necessary.
834 * Return -1 on failure
835 * Return the socket identifier
838 change_socket(void) {
839 int s, *sockp;
841 switch (server_ai->ai_family) {
842 case AF_INET:
843 sockp = &socket4;
844 break;
845 #ifdef AF_INET6
846 case AF_INET6:
847 sockp = &socket6;
848 break;
849 #endif
850 default:
851 fprintf(stderr, "unexpected address family: %d\n",
852 server_ai->ai_family);
853 exit(1);
856 if (*sockp == -1) {
857 if ((s = open_socket()) == -1)
858 return (-1);
859 *sockp = s;
862 return (*sockp);
866 * reset_rttarray:
867 * (re)allocate RTT array and zero-clear the whole buffer.
868 * if array is being used, it is freed.
869 * Returns -1 on failure
870 * Returns a non-negative integer otherwise
873 reset_rttarray(int size) {
874 if (rttarray != NULL)
875 free(rttarray);
876 if (rttarray_interval != NULL)
877 free(rttarray_interval);
879 rttarray = NULL;
880 rttarray_interval = NULL;
881 rtt_max = -1;
882 rtt_min = -1;
884 if (size > 0) {
885 rttarray = malloc(size * sizeof(rttarray[0]));
886 if (rttarray == NULL) {
887 fprintf(stderr,
888 "Error: allocating memory for RTT array\n");
889 return (-1);
891 memset(rttarray, 0, size * sizeof(rttarray[0]));
893 rttarray_interval = malloc(size *
894 sizeof(rttarray_interval[0]));
895 if (rttarray_interval == NULL) {
896 fprintf(stderr,
897 "Error: allocating memory for RTT array\n");
898 return (-1);
901 memset(rttarray_interval, 0,
902 size * sizeof(rttarray_interval[0]));
905 return (0);
909 * set_query_interval:
910 * set the interval of consecutive queries if the target qps are specified.
911 * Returns -1 on failure
912 * Returns a non-negative integer otherwise
915 set_query_interval(unsigned int qps) {
916 if (qps == 0)
917 return (0);
919 query_interval = (1.0 / (double)qps);
921 return (0);
925 * setup:
926 * Set configuration options from command line arguments
927 * Open datafile ready for reading
929 * Return -1 on failure
930 * Return non-negative integer on success
933 setup(int argc, char **argv) {
934 set_input_stdin();
936 if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING) == -1) {
937 fprintf(stderr, "%s: Unable to set default max outstanding "
938 "queries\n", argv[0]);
939 return (-1);
942 if (set_server(DEF_SERVER_TO_QUERY) == -1) {
943 fprintf(stderr, "%s: Error setting default server name\n",
944 argv[0]);
945 return (-1);
948 if (set_server_port(DEF_SERVER_PORT) == -1) {
949 fprintf(stderr, "%s: Error setting default server port\n",
950 argv[0]);
951 return (-1);
954 if (parse_args(argc, argv) == -1) {
955 show_usage();
956 return (-1);
959 if (open_datafile() == -1)
960 return (-1);
962 if (set_server_sa() == -1)
963 return (-1);
965 if ((query_socket = change_socket()) == -1)
966 return (-1);
968 if (reset_rttarray(rttarray_size) == -1)
969 return (-1);
971 if (set_query_interval(target_qps) == -1)
972 return (-1);
974 return (0);
978 * set_timenow:
979 * Set a timeval struct to indicate the current time
981 void
982 set_timenow(struct timeval *tv) {
983 if (gettimeofday(tv, NULL) == -1) {
984 fprintf(stderr, "Error in gettimeofday(). Using inaccurate "
985 "time() instead\n");
986 tv->tv_sec = time(NULL);
987 tv->tv_usec = 0;
992 * addtv:
993 * add tv1 and tv2, store the result in tv_result.
995 void
996 addtv(struct timeval *tv1, struct timeval *tv2, struct timeval *tv_result) {
997 tv_result->tv_sec = tv1->tv_sec + tv2->tv_sec;
998 tv_result->tv_usec = tv1->tv_usec + tv2->tv_usec;
999 if (tv_result->tv_usec > 1000000) {
1000 tv_result->tv_sec++;
1001 tv_result->tv_usec -= 1000000;
1006 * difftv:
1007 * Find the difference in seconds between two timeval structs.
1009 * Return the difference between tv1 and tv2 in seconds in a double.
1011 double
1012 difftv(struct timeval tv1, struct timeval tv2) {
1013 long diff_sec, diff_usec;
1014 double diff;
1016 diff_sec = tv1.tv_sec - tv2.tv_sec;
1017 diff_usec = tv1.tv_usec - tv2.tv_usec;
1019 diff = (double)diff_sec + ((double)diff_usec / 1000000.0);
1021 return (diff);
1025 * timelimit_reached:
1026 * Have we reached the time limit (if any)?
1028 * Returns FALSE if there is no time limit or if we have not reached it
1029 * Returns TRUE otherwise
1032 timelimit_reached(void) {
1033 struct timeval time_now;
1035 set_timenow(&time_now);
1037 if (use_timelimit == FALSE)
1038 return (FALSE);
1040 if (setup_phase == TRUE) {
1041 if (difftv(time_now, time_of_program_start)
1042 < (double)(run_timelimit + HARD_TIMEOUT_EXTRA))
1043 return (FALSE);
1044 else
1045 return (TRUE);
1046 } else {
1047 if (difftv(time_now, time_of_first_query)
1048 < (double)run_timelimit)
1049 return (FALSE);
1050 else
1051 return (TRUE);
1056 * keep_sending:
1057 * Should we keep sending queries or stop here?
1059 * Return TRUE if we should keep on sending queries
1060 * Return FALSE if we should stop
1062 * Side effects:
1063 * Rewinds the input and clears reached_end_input if we have reached the
1064 * end of the input, but we are meant to run through it multiple times
1065 * and have not hit the time limit yet (if any is set).
1068 keep_sending(int *reached_end_input) {
1069 static int stop = FALSE;
1071 if (stop == TRUE)
1072 return (FALSE);
1074 if ((*reached_end_input == FALSE) && (timelimit_reached() == FALSE))
1075 return (TRUE);
1076 else if ((*reached_end_input == TRUE) && (run_only_once == FALSE)
1077 && (timelimit_reached() == FALSE)) {
1078 rewind(datafile_ptr);
1079 *reached_end_input = FALSE;
1080 runs_through_file++;
1081 return (TRUE);
1082 } else {
1083 if (*reached_end_input == TRUE)
1084 runs_through_file++;
1085 set_timenow(&time_of_stop_sending);
1086 stop = TRUE;
1087 return (FALSE);
1092 * queries_outstanding:
1093 * How many queries are outstanding?
1095 * Returns the number of outstanding queries
1097 unsigned int
1098 queries_outstanding(void) {
1099 return (num_queries_outstanding);
1103 * next_input_line:
1104 * Get the next non-comment line from the input file
1106 * Put text in line, up to max of n chars. Skip comment lines.
1107 * Skip empty lines.
1109 * Return line length on success
1110 * Return 0 if cannot read a non-comment line (EOF or error)
1113 next_input_line(char *line, int n) {
1114 char *result;
1116 do {
1117 result = fgets(line, n, datafile_ptr);
1118 } while ((result != NULL) &&
1119 ((line[0] == COMMENT_CHAR) || (line[0] == '\n')));
1121 if (result == NULL)
1122 return (0);
1123 else
1124 return (strlen(line));
1128 * identify_directive:
1129 * Gives us a numerical value equivelant for a directive string
1131 * Returns the value for the directive
1132 * Returns -1 if not a valid directive
1135 identify_directive(char *dir) {
1136 static char *directives[] = DIRECTIVES;
1137 static int dir_values[] = DIR_VALUES;
1138 unsigned int index, num_directives;
1140 num_directives = sizeof(directives) / sizeof(directives[0]);
1142 if (num_directives > (sizeof(dir_values) / sizeof(int)))
1143 num_directives = sizeof(dir_values) / sizeof(int);
1145 for (index = 0; index < num_directives; index++) {
1146 if (strcmp(dir, directives[index]) == 0)
1147 return (dir_values[index]);
1150 return (-1);
1154 * update_config:
1155 * Update configuration options from a line from the input file
1157 void
1158 update_config(char *config_change_desc) {
1159 char *directive, *config_value, *trailing_garbage;
1160 char conf_copy[MAX_INPUT_LEN + 1];
1161 unsigned int uint_val;
1162 int directive_number;
1163 int check;
1164 int old_af;
1166 if (ignore_config_changes == TRUE) {
1167 fprintf(stderr, "Ignoring configuration change: %s",
1168 config_change_desc);
1169 return;
1172 strcpy(conf_copy, config_change_desc);
1174 ++config_change_desc;
1176 if (*config_change_desc == '\0') {
1177 fprintf(stderr, "Invalid config: No directive present: %s\n",
1178 conf_copy);
1179 return;
1182 if (index(WHITESPACE, *config_change_desc) != NULL) {
1183 fprintf(stderr, "Invalid config: Space before directive or "
1184 "no directive present: %s\n", conf_copy);
1185 return;
1188 directive = strtok(config_change_desc, WHITESPACE);
1189 config_value = strtok(NULL, WHITESPACE);
1190 trailing_garbage = strtok(NULL, WHITESPACE);
1192 if ((directive_number = identify_directive(directive)) == -1) {
1193 fprintf(stderr, "Invalid config: Bad directive: %s\n",
1194 conf_copy);
1195 return;
1198 if (config_value == NULL) {
1199 fprintf(stderr, "Invalid config: No value present: %s\n",
1200 conf_copy);
1201 return;
1204 if (trailing_garbage != NULL) {
1205 fprintf(stderr, "Config warning: "
1206 "trailing garbage: %s\n", conf_copy);
1209 switch(directive_number) {
1211 case V_SERVER:
1212 if (serverset && (setup_phase == TRUE)) {
1213 fprintf(stderr, "Config change overridden by command "
1214 "line: %s\n", directive);
1215 return;
1218 if (set_server(config_value) == -1) {
1219 fprintf(stderr, "Set server error: unable to change "
1220 "the server name to '%s'\n", config_value);
1221 return;
1224 old_af = server_ai->ai_family;
1225 if (set_server_sa() == -1) {
1226 fprintf(stderr, "Set server error: unable to resolve "
1227 "a new server '%s'\n",
1228 config_value);
1229 return;
1231 if (old_af != server_ai->ai_family) {
1232 if ((query_socket = change_socket()) == -1) {
1233 /* XXX: this is fatal */
1234 fprintf(stderr, "Set server error: "
1235 "unable to open a new socket "
1236 "for '%s'\n", config_value);
1237 exit(1);
1241 break;
1243 case V_PORT:
1244 if (portset && (setup_phase == TRUE)) {
1245 fprintf(stderr, "Config change overridden by command "
1246 "line: %s\n", directive);
1247 return;
1250 check = is_uint(config_value, &uint_val);
1252 if ((check == TRUE) && (uint_val > 0)) {
1253 if (set_server_port(config_value) == -1) {
1254 fprintf(stderr, "Invalid config: Bad value for"
1255 " %s: %s\n", directive, config_value);
1256 } else {
1257 if (set_server_sa() == -1) {
1258 fprintf(stderr,
1259 "Failed to set a new port\n");
1260 return;
1263 } else
1264 fprintf(stderr, "Invalid config: Bad value for "
1265 "%s: %s\n", directive, config_value);
1266 break;
1268 case V_MAXQUERIES:
1269 if (queriesset && (setup_phase == TRUE)) {
1270 fprintf(stderr, "Config change overridden by command "
1271 "line: %s\n", directive);
1272 return;
1275 check = is_uint(config_value, &uint_val);
1277 if ((check == TRUE) && (uint_val > 0)) {
1278 set_max_queries(uint_val);
1279 } else
1280 fprintf(stderr, "Invalid config: Bad value for "
1281 "%s: %s\n", directive, config_value);
1282 break;
1284 case V_MAXWAIT:
1285 if (timeoutset && (setup_phase == TRUE)) {
1286 fprintf(stderr, "Config change overridden by command "
1287 "line: %s\n", directive);
1288 return;
1291 check = is_uint(config_value, &uint_val);
1293 if ((check == TRUE) && (uint_val > 0)) {
1294 query_timeout = uint_val;
1295 } else
1296 fprintf(stderr, "Invalid config: Bad value for "
1297 "%s: %s\n", directive, config_value);
1298 break;
1300 default:
1301 fprintf(stderr, "Invalid config: Bad directive: %s\n",
1302 directive);
1303 break;
1308 * parse_query:
1309 * Parse a query line from the input file
1311 * Set qname to be the domain to query (up to a max of qnlen chars)
1312 * Set qtype to be the type of the query
1314 * Return -1 on failure
1315 * Return a non-negative integer otherwise
1318 parse_query(char *input, char *qname, int qnlen, int *qtype) {
1319 static char *qtype_strings[] = QTYPE_STRINGS;
1320 static int qtype_codes[] = QTYPE_CODES;
1321 int num_types, index;
1322 int found = FALSE;
1323 char incopy[MAX_INPUT_LEN + 1];
1324 char *domain_str, *type_str;
1326 num_types = sizeof(qtype_strings) / sizeof(qtype_strings[0]);
1327 if (num_types > (sizeof(qtype_codes) / sizeof(int)))
1328 num_types = sizeof(qtype_codes) / sizeof(int);
1330 strcpy(incopy, input);
1332 domain_str = strtok(incopy, WHITESPACE);
1333 type_str = strtok(NULL, WHITESPACE);
1335 if ((domain_str == NULL) || (type_str == NULL)) {
1336 fprintf(stderr, "Invalid query input format: %s\n", input);
1337 return (-1);
1340 if (strlen(domain_str) > qnlen) {
1341 fprintf(stderr, "Query domain too long: %s\n", domain_str);
1342 return (-1);
1345 for (index = 0; (index < num_types) && (found == FALSE); index++) {
1346 if (strcasecmp(type_str, qtype_strings[index]) == 0) {
1347 *qtype = qtype_codes[index];
1348 found = TRUE;
1352 if (found == FALSE) {
1353 fprintf(stderr, "Query type not understood: %s\n", type_str);
1354 return (-1);
1357 strcpy(qname, domain_str);
1359 return (0);
1363 * dispatch_query:
1364 * Send the query packet for the entry
1366 * Return -1 on failure
1367 * Return a non-negative integer otherwise
1370 dispatch_query(unsigned short int id, char *dom, int qt) {
1371 static u_char packet_buffer[PACKETSZ + 1];
1372 static socklen_t sockaddrlen = sizeof(struct sockaddr);
1373 int buffer_len = PACKETSZ;
1374 int bytes_sent;
1375 unsigned short int net_id = htons(id);
1376 char *id_ptr = (char *)&net_id;
1378 buffer_len = res_mkquery(QUERY, dom, C_IN, qt, NULL, 0,
1379 NULL, packet_buffer, PACKETSZ);
1380 if (buffer_len == -1) {
1381 fprintf(stderr, "Failed to create query packet: %s %d\n",
1382 dom, qt);
1383 return (-1);
1385 if (edns) {
1386 unsigned char *p;
1387 if (buffer_len + EDNSLEN >= PACKETSZ) {
1388 fprintf(stderr, "Failed to add OPT to query packet\n");
1389 return (-1);
1391 packet_buffer[11] = 1;
1392 p = &packet_buffer[buffer_len];
1393 *p++ = 0; /* root name */
1394 *p++ = 0;
1395 *p++ = 41; /* OPT */
1396 *p++ = 16;
1397 *p++ = 0; /* UDP payload size (4K) */
1398 *p++ = 0; /* extended rcode */
1399 *p++ = 0; /* version */
1400 if (dnssec)
1401 *p++ = 0x80; /* upper flag bits - DO set */
1402 else
1403 *p++ = 0; /* upper flag bits */
1404 *p++ = 0; /* lower flag bit */
1405 *p++ = 0;
1406 *p++ = 0; /* rdlen == 0 */
1407 buffer_len += EDNSLEN;
1410 packet_buffer[0] = id_ptr[0];
1411 packet_buffer[1] = id_ptr[1];
1413 bytes_sent = sendto(query_socket, packet_buffer, buffer_len, 0,
1414 server_ai->ai_addr, server_ai->ai_addrlen);
1415 if (bytes_sent == -1) {
1416 fprintf(stderr, "Failed to send query packet: %s %d\n",
1417 dom, qt);
1418 return (-1);
1421 if (bytes_sent != buffer_len)
1422 fprintf(stderr, "Warning: incomplete packet sent: %s %d\n",
1423 dom, qt);
1425 return (0);
1429 * send_query:
1430 * Send a query based on a line of input
1432 void
1433 send_query(char *query_desc) {
1434 static unsigned short int use_query_id = 0;
1435 static int qname_len = MAX_DOMAIN_LEN;
1436 static char domain[MAX_DOMAIN_LEN + 1];
1437 char serveraddr[NI_MAXHOST];
1438 int query_type;
1439 unsigned int count;
1441 use_query_id++;
1443 if (parse_query(query_desc, domain, qname_len, &query_type) == -1) {
1444 fprintf(stderr, "Error parsing query: %s\n", query_desc);
1445 return;
1448 if (dispatch_query(use_query_id, domain, query_type) == -1) {
1449 char *addrstr;
1451 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen,
1452 serveraddr, sizeof(serveraddr), NULL, 0,
1453 NI_NUMERICHOST) == 0) {
1454 addrstr = serveraddr;
1455 } else
1456 addrstr = "???"; /* XXX: this should not happen */
1457 fprintf(stderr, "Error sending query to %s: %s\n",
1458 addrstr, query_desc);
1459 return;
1462 if (setup_phase == TRUE) {
1463 set_timenow(&time_of_first_query);
1464 time_of_first_query_sec = (double)time_of_first_query.tv_sec +
1465 ((double)time_of_first_query.tv_usec / 1000000.0);
1466 setup_phase = FALSE;
1467 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen,
1468 serveraddr, sizeof(serveraddr), NULL, 0,
1469 NI_NUMERICHOST) != 0) {
1470 fprintf(stderr, "Error printing server address\n");
1471 return;
1473 printf("[Status] Sending queries (beginning with %s)\n",
1474 serveraddr);
1477 /* Find the first slot in status[] that is not in use */
1478 for (count = 0; (status[count].in_use == TRUE)
1479 && (count < max_queries_outstanding); count++);
1481 if (status[count].in_use == TRUE) {
1482 fprintf(stderr, "Unexpected error: We have run out of "
1483 "status[] space!\n");
1484 return;
1487 /* Register the query in status[] */
1488 status[count].in_use = TRUE;
1489 status[count].id = use_query_id;
1490 if (verbose)
1491 status[count].desc = strdup(query_desc);
1492 set_timenow(&status[count].sent_timestamp);
1494 if (num_queries_sent_interval == 0)
1495 set_timenow(&time_of_first_query_interval);
1497 num_queries_sent++;
1498 num_queries_sent_interval++;
1499 num_queries_outstanding++;
1502 void
1503 register_rtt(struct timeval *timestamp) {
1504 int i;
1505 int oldquery = FALSE;
1506 struct timeval now;
1507 double rtt;
1509 set_timenow(&now);
1510 rtt = difftv(now, *timestamp);
1512 if (difftv(*timestamp, time_of_first_query_interval) < 0)
1513 oldquery = TRUE;
1515 if (rtt_max < 0 || rtt_max < rtt)
1516 rtt_max = rtt;
1518 if (rtt_min < 0 || rtt_min > rtt)
1519 rtt_min = rtt;
1521 rtt_total += rtt;
1523 if (!oldquery) {
1524 if (rtt_max_interval < 0 || rtt_max_interval < rtt)
1525 rtt_max_interval = rtt;
1527 if (rtt_min_interval < 0 || rtt_min_interval > rtt)
1528 rtt_min_interval = rtt;
1530 rtt_total_interval += rtt;
1533 if (rttarray == NULL)
1534 return;
1536 i = (int)(rtt * (1000000.0 / rttarray_unit));
1537 if (i < rttarray_size) {
1538 rttarray[i]++;
1539 if (!oldquery)
1540 rttarray_interval[i]++;
1541 } else {
1542 fprintf(stderr, "Warning: RTT is out of range: %.6lf\n",
1543 rtt);
1544 rtt_overflows++;
1545 if (!oldquery)
1546 rtt_overflows_interval++;
1551 * register_response:
1552 * Register receipt of a query
1554 * Removes (sets in_use = FALSE) the record for the given query id in
1555 * status[] if any exists.
1557 void
1558 register_response(unsigned short int id, unsigned int rcode) {
1559 unsigned int ct = 0;
1560 int found = FALSE;
1561 struct timeval now;
1562 double rtt;
1564 for (; (ct < query_status_allocated) && (found == FALSE); ct++) {
1565 if ((status[ct].in_use == TRUE) && (status[ct].id == id)) {
1566 status[ct].in_use = FALSE;
1567 num_queries_outstanding--;
1568 found = TRUE;
1570 register_rtt(&status[ct].sent_timestamp);
1572 if (status[ct].desc) {
1573 printf("> %s %s\n", rcode_strings[rcode],
1574 status[ct].desc);
1575 free(status[ct].desc);
1577 if (countrcodes)
1578 rcodecounts[rcode]++;
1582 if (found == FALSE) {
1583 if (target_qps > 0) {
1584 num_queries_possiblydelayed++;
1585 num_queries_possiblydelayed_interval++;
1586 } else {
1587 fprintf(stderr, "Warning: Received a response with an "
1588 "unexpected (maybe timed out) id: %u\n", id);
1594 * process_single_response:
1595 * Receive from the given socket & process an invididual response packet.
1596 * Remove it from the list of open queries (status[]) and decrement the
1597 * number of outstanding queries if it matches an open query.
1599 void
1600 process_single_response(int sockfd) {
1601 struct sockaddr_storage from_addr_ss;
1602 struct sockaddr *from_addr;
1603 static unsigned char in_buf[MAX_BUFFER_LEN];
1604 int numbytes, addr_len, resp_id;
1605 int flags;
1607 memset(&from_addr_ss, 0, sizeof(from_addr_ss));
1608 from_addr = (struct sockaddr *)&from_addr_ss;
1609 addr_len = sizeof(from_addr_ss);
1611 if ((numbytes = recvfrom(sockfd, in_buf, MAX_BUFFER_LEN,
1612 0, from_addr, &addr_len)) == -1) {
1613 fprintf(stderr, "Error receiving datagram\n");
1614 return;
1617 resp_id = get_uint16(in_buf);
1618 flags = get_uint16(in_buf + 2);
1620 register_response(resp_id, flags & 0xF);
1624 * data_available:
1625 * Is there data available on the given file descriptor?
1627 * Return TRUE if there is
1628 * Return FALSE otherwise
1631 data_available(double wait) {
1632 fd_set read_fds;
1633 struct timeval tv;
1634 int retval;
1635 int available = FALSE;
1636 int maxfd = -1;
1638 /* Set list of file descriptors */
1639 FD_ZERO(&read_fds);
1640 if (socket4 != -1) {
1641 FD_SET(socket4, &read_fds);
1642 maxfd = socket4;
1644 if (socket6 != -1) {
1645 FD_SET(socket6, &read_fds);
1646 if (maxfd == -1 || maxfd < socket6)
1647 maxfd = socket6;
1650 if ((wait > 0.0) && (wait < (double)LONG_MAX)) {
1651 tv.tv_sec = (long)floor(wait);
1652 tv.tv_usec = (long)(1000000.0 * (wait - floor(wait)));
1653 } else {
1654 tv.tv_sec = 0;
1655 tv.tv_usec = 0;
1658 retval = select(maxfd + 1, &read_fds, NULL, NULL, &tv);
1660 if (socket4 != -1 && FD_ISSET(socket4, &read_fds)) {
1661 available = TRUE;
1662 process_single_response(socket4);
1664 if (socket6 != -1 && FD_ISSET(socket6, &read_fds)) {
1665 available = TRUE;
1666 process_single_response(socket6);
1669 return (available);
1673 * process_responses:
1674 * Go through any/all received responses and remove them from the list of
1675 * open queries (set in_use = FALSE for their entry in status[]), also
1676 * decrementing the number of outstanding queries.
1678 void
1679 process_responses(int adjust_rate) {
1680 double wait;
1681 struct timeval now, waituntil;
1682 double first_packet_wait = RESPONSE_BLOCKING_WAIT_TIME;
1683 unsigned int outstanding = queries_outstanding();
1685 if (adjust_rate == TRUE) {
1686 double u;
1688 u = time_of_first_query_sec +
1689 query_interval * num_queries_sent;
1690 waituntil.tv_sec = (long)floor(u);
1691 waituntil.tv_usec = (long)(1000000.0 * (u - waituntil.tv_sec));
1694 * Wait until a response arrives or the specified limit is
1695 * reached.
1697 while (1) {
1698 set_timenow(&now);
1699 wait = difftv(waituntil, now);
1700 if (wait <= 0)
1701 wait = 0.0;
1702 if (data_available(wait) != TRUE)
1703 break;
1706 * We have reached the limit. Read as many responses
1707 * as possible without waiting, and exit.
1709 if (wait == 0) {
1710 while (data_available(0.0) == TRUE)
1712 break;
1715 } else {
1717 * Don't block waiting for packets at all if we aren't
1718 * looking for any responses or if we are now able to send new
1719 * queries.
1721 if ((outstanding == 0) ||
1722 (outstanding < max_queries_outstanding)) {
1723 first_packet_wait = 0.0;
1726 if (data_available(first_packet_wait) == TRUE) {
1727 while (data_available(0.0) == TRUE)
1734 * retire_old_queries:
1735 * Go through the list of open queries (status[]) and remove any queries
1736 * (i.e. set in_use = FALSE) which are older than the timeout, decrementing
1737 * the number of queries outstanding for each one removed.
1739 void
1740 retire_old_queries(int sending) {
1741 unsigned int count = 0;
1742 struct timeval curr_time;
1743 double timeout = query_timeout;
1744 int timeout_reduced = FALSE;
1747 * If we have target qps and would not be able to send any packets
1748 * due to buffer full, check whether we are behind the schedule.
1749 * If we are, purge some queries more aggressively.
1751 if (target_qps > 0 && sending == TRUE && count == 0 &&
1752 queries_outstanding() == max_queries_outstanding) {
1753 struct timeval next, now;
1754 double n;
1756 n = time_of_first_query_sec +
1757 query_interval * num_queries_sent;
1758 next.tv_sec = (long)floor(n);
1759 next.tv_usec = (long)(1000000.0 * (n - next.tv_sec));
1761 set_timenow(&now);
1762 if (difftv(next, now) <= 0) {
1763 timeout_reduced = TRUE;
1764 timeout = 0.001; /* XXX: ad-hoc value */
1768 set_timenow(&curr_time);
1770 for (; count < query_status_allocated; count++) {
1772 if ((status[count].in_use == TRUE)
1773 && (difftv(curr_time, status[count].sent_timestamp)
1774 >= (double)timeout)) {
1776 status[count].in_use = FALSE;
1777 num_queries_outstanding--;
1778 num_queries_timed_out++;
1779 num_queries_timed_out_interval++;
1781 if (timeout_reduced == FALSE) {
1782 if (status[count].desc) {
1783 printf("> T %s\n", status[count].desc);
1784 free(status[count].desc);
1785 } else {
1786 printf("[Timeout] Query timed out: "
1787 "msg id %u\n",
1788 status[count].id);
1796 * print_histogram
1797 * Print RTT histogram to the specified file in the gnuplot format
1799 void
1800 print_histogram(unsigned int total) {
1801 int i;
1802 double ratio;
1803 FILE *fp;
1805 if (rtt_histogram_file == NULL || rttarray == NULL)
1806 return;
1808 fp = fopen((const char *)rtt_histogram_file, "w+");
1809 if (fp == NULL) {
1810 fprintf(stderr, "Error opening RTT histogram file: %s\n",
1811 rtt_histogram_file);
1812 return;
1815 for (i = 0; i < rttarray_size; i++) {
1816 ratio = ((double)rttarray[i] / (double)total) * 100;
1817 fprintf(fp, "%.6lf %.3lf\n",
1818 (double)(i * rttarray_unit) +
1819 (double)rttarray_unit / 2,
1820 ratio);
1823 (void)fclose(fp);
1827 * print_statistics:
1828 * Print out statistics based on the results of the test
1830 void
1831 print_statistics(int intermediate, unsigned int sent, unsigned int timed_out,
1832 unsigned int possibly_delayed,
1833 struct timeval *first_query,
1834 struct timeval *program_start,
1835 struct timeval *end_perf, struct timeval *end_query,
1836 double rmax, double rmin, double rtotal,
1837 unsigned int roverflows, unsigned int *rarray)
1839 unsigned int num_queries_completed;
1840 double per_lost, per_completed, per_lost2, per_completed2;
1841 double run_time, queries_per_sec, queries_per_sec2;
1842 double queries_per_sec_total;
1843 double rtt_average, rtt_stddev;
1844 struct timeval start_time;
1846 num_queries_completed = sent - timed_out;
1848 if (num_queries_completed == 0) {
1849 per_lost = 0.0;
1850 per_completed = 0.0;
1852 per_lost2 = 0.0;
1853 per_completed2 = 0.0;
1854 } else {
1855 per_lost = (100.0 * (double)timed_out) / (double)sent;
1856 per_completed = 100.0 - per_lost;
1858 per_lost2 = (100.0 * (double)(timed_out - possibly_delayed))
1859 / (double)sent;
1860 per_completed2 = 100 - per_lost2;
1863 if (sent == 0) {
1864 start_time.tv_sec = program_start->tv_sec;
1865 start_time.tv_usec = program_start->tv_usec;
1866 run_time = 0.0;
1867 queries_per_sec = 0.0;
1868 queries_per_sec2 = 0.0;
1869 queries_per_sec_total = 0.0;
1870 } else {
1871 start_time.tv_sec = first_query->tv_sec;
1872 start_time.tv_usec = first_query->tv_usec;
1873 run_time = difftv(*end_perf, *first_query);
1874 queries_per_sec = (double)num_queries_completed / run_time;
1875 queries_per_sec2 = (double)(num_queries_completed +
1876 possibly_delayed) / run_time;
1878 queries_per_sec_total = (double)sent /
1879 difftv(*end_query, *first_query);
1882 if (num_queries_completed > 0) {
1883 int i;
1884 double sum = 0;
1886 rtt_average = rtt_total / (double)num_queries_completed;
1887 for (i = 0; i < rttarray_size; i++) {
1888 if (rarray[i] != 0) {
1889 double mean, diff;
1891 mean = (double)(i * rttarray_unit) +
1892 (double)rttarray_unit / 2;
1893 diff = rtt_average - (mean / 1000000.0);
1894 sum += (diff * diff) * rarray[i];
1897 rtt_stddev = sqrt(sum / (double)num_queries_completed);
1898 } else {
1899 rtt_average = 0.0;
1900 rtt_stddev = 0.0;
1903 printf("\n");
1905 printf("%sStatistics:\n", intermediate ? "Intermediate " : "");
1907 printf("\n");
1909 if (!intermediate) {
1910 printf(" Parse input file: %s\n",
1911 ((run_only_once == TRUE) ? "once" : "multiple times"));
1912 if (use_timelimit)
1913 printf(" Run time limit: %u seconds\n",
1914 run_timelimit);
1915 if (run_only_once == FALSE)
1916 printf(" Ran through file: %u times\n",
1917 runs_through_file);
1918 else
1919 printf(" Ended due to: reaching %s\n",
1920 ((runs_through_file == 0) ? "time limit"
1921 : "end of file"));
1923 printf("\n");
1926 printf(" Queries sent: %u queries\n", sent);
1927 printf(" Queries completed: %u queries\n", num_queries_completed);
1928 printf(" Queries lost: %u queries\n", timed_out);
1929 printf(" Queries delayed(?): %u queries\n", possibly_delayed);
1931 printf("\n");
1933 printf(" RTT max: %3.6lf sec\n", rmax);
1934 printf(" RTT min: %3.6lf sec\n", rmin);
1935 printf(" RTT average: %3.6lf sec\n", rtt_average);
1936 printf(" RTT std deviation: %3.6lf sec\n", rtt_stddev);
1937 printf(" RTT out of range: %u queries\n", roverflows);
1939 if (!intermediate) /* XXX should we print this case also? */
1940 print_histogram(num_queries_completed);
1942 printf("\n");
1944 if (countrcodes) {
1945 unsigned int i;
1947 for (i = 0; i < 16; i++) {
1948 if (rcodecounts[i] == 0)
1949 continue;
1950 printf(" Returned %8s: %u queries\n",
1951 rcode_strings[i], rcodecounts[i]);
1953 printf("\n");
1956 printf(" Percentage completed: %6.2lf%%\n", per_completed);
1957 if (possibly_delayed > 0)
1958 printf(" (w/ delayed qrys): %6.2lf%%\n", per_completed2);
1959 printf(" Percentage lost: %6.2lf%%\n", per_lost);
1960 if (possibly_delayed > 0)
1961 printf(" (w/o delayed qrys): %6.2lf%%\n", per_lost2);
1963 printf("\n");
1965 printf(" Started at: %s",
1966 ctime((const time_t *)&start_time.tv_sec));
1967 printf(" Finished at: %s",
1968 ctime((const time_t *)&end_perf->tv_sec));
1969 printf(" Ran for: %.6lf seconds\n", run_time);
1971 printf("\n");
1973 printf(" Queries per second: %.6lf qps\n", queries_per_sec);
1974 if (possibly_delayed > 0) {
1975 printf(" (w/ delayed qrys): %.6lf qps\n",
1976 queries_per_sec2);
1978 if (target_qps > 0) {
1979 printf(" Total QPS/target: %.6lf/%d qps\n",
1980 queries_per_sec_total, target_qps);
1983 printf("\n");
1986 void
1987 print_interval_statistics() {
1988 struct timeval time_now;
1990 if (use_timelimit == FALSE)
1991 return;
1993 if (setup_phase == TRUE)
1994 return;
1996 if (print_interval == 0)
1997 return;
1999 if (timelimit_reached() == TRUE)
2000 return;
2002 set_timenow(&time_now);
2003 if (difftv(time_now, time_of_first_query_interval)
2004 <= (double)print_interval)
2005 return;
2007 /* Don't count currently outstanding queries */
2008 num_queries_sent_interval -= queries_outstanding();
2009 print_statistics(TRUE, num_queries_sent_interval,
2010 num_queries_timed_out_interval,
2011 num_queries_possiblydelayed_interval,
2012 &time_of_first_query_interval,
2013 &time_of_first_query_interval, &time_now, &time_now,
2014 rtt_max_interval, rtt_min_interval,
2015 rtt_total_interval, rtt_overflows_interval,
2016 rttarray_interval);
2018 /* Reset intermediate counters */
2019 num_queries_sent_interval = 0;
2020 num_queries_timed_out_interval = 0;
2021 num_queries_possiblydelayed_interval = 0;
2022 rtt_max_interval = -1;
2023 rtt_min_interval = -1;
2024 rtt_total_interval = 0.0;
2025 rtt_overflows_interval = 0;
2026 if (rttarray_interval != NULL) {
2027 memset(rttarray_interval, 0,
2028 sizeof(rttarray_interval[0]) * rttarray_size);
2033 * queryperf Program Mainline
2036 main(int argc, char **argv) {
2037 int adjust_rate;
2038 int sending = FALSE;
2039 int got_eof = FALSE;
2040 int input_length = MAX_INPUT_LEN;
2041 char input_line[MAX_INPUT_LEN + 1];
2043 set_timenow(&time_of_program_start);
2044 time_of_first_query.tv_sec = 0;
2045 time_of_first_query.tv_usec = 0;
2046 time_of_first_query_interval.tv_sec = 0;
2047 time_of_first_query_interval.tv_usec = 0;
2048 time_of_end_of_run.tv_sec = 0;
2049 time_of_end_of_run.tv_usec = 0;
2051 input_line[0] = '\0';
2053 show_startup_info();
2055 if (setup(argc, argv) == -1)
2056 return (-1);
2058 printf("[Status] Processing input data\n");
2060 while ((sending = keep_sending(&got_eof)) == TRUE ||
2061 queries_outstanding() > 0) {
2062 print_interval_statistics();
2063 adjust_rate = FALSE;
2065 while ((sending = keep_sending(&got_eof)) == TRUE &&
2066 queries_outstanding() < max_queries_outstanding) {
2067 int len = next_input_line(input_line, input_length);
2068 if (len == 0) {
2069 got_eof = TRUE;
2070 } else {
2071 /* Zap the trailing newline */
2072 if (input_line[len - 1] == '\n')
2073 input_line[len - 1] = '\0';
2076 * TODO: Should test if we got a whole line
2077 * and flush to the next \n in input if not
2078 * here... Add this later. Only do the next
2079 * few lines if we got a whole line, else
2080 * print a warning. Alternative: Make the
2081 * max line size really big. BAD! :)
2084 if (input_line[0] == CONFIG_CHAR)
2085 update_config(input_line);
2086 else {
2087 send_query(input_line);
2088 if (target_qps > 0 &&
2089 (num_queries_sent %
2090 max_queries_outstanding) == 0) {
2091 adjust_rate = TRUE;
2097 process_responses(adjust_rate);
2098 retire_old_queries(sending);
2101 set_timenow(&time_of_end_of_run);
2103 printf("[Status] Testing complete\n");
2105 close_socket();
2106 close_datafile();
2108 print_statistics(FALSE, num_queries_sent, num_queries_timed_out,
2109 num_queries_possiblydelayed,
2110 &time_of_first_query, &time_of_program_start,
2111 &time_of_end_of_run, &time_of_stop_sending,
2112 rtt_max, rtt_min, rtt_total, rtt_overflows, rttarray);
2114 return (0);