etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / contrib / queryperf / queryperf.c
blobb9da1653ad3409a67af4e4838115d13ead8208a6
1 /* $NetBSD: queryperf.c,v 1.6 2014/12/10 04:37:56 christos Exp $ */
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 #define BIND_8_COMPAT /* Pull in <arpa/nameser_compat.h> */
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>
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #ifndef HAVE_GETADDRINFO
49 #include "missing/addrinfo.h"
50 #endif
51 #endif
54 * Configuration defaults
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 */
63 #define DEF_RTTARRAY_SIZE 50000
64 #define DEF_RTTARRAY_UNIT 100 /* in usec */
67 * Other constants / definitions
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
81 #define FALSE 0
82 #define TRUE 1
84 #define WHITESPACE " \t\n"
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 }
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" \
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 \
113 #define RCODE_STRINGS { \
114 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \
115 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \
116 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \
117 "rcode12", "rcode13", "rcode14", "rcode15" \
121 * Data type definitions
124 #define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */
125 #define VALID_QUERY_STATUS(q) ((q) != NULL && \
126 (q)->magic == QUERY_STATUS_MAGIC)
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];
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];
145 * Forward declarations.
147 int is_uint(char *test_int, unsigned int *result);
150 * Configuration options (global)
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;
158 int family = AF_UNSPEC;
159 int use_stdin = TRUE;
160 char *datafile_name; /* init NULL */
162 char *server_to_query; /* init NULL */
163 char *server_port; /* init NULL */
164 struct addrinfo *server_ai; /* init NULL */
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 */
171 unsigned int target_qps; /* init 0 */
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};
179 int verbose = FALSE;
180 int recurse = 1;
183 * Other global stuff
186 int setup_phase = TRUE;
188 FILE *datafile_ptr; /* init NULL */
189 unsigned int runs_through_file; /* init 0 */
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;
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;
206 struct timeval time_of_queryset_start;
207 double query_interval;
208 struct timeval time_of_next_queryset;
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;
226 struct query_status *status; /* init NULL */
227 unsigned int query_status_allocated; /* init 0 */
229 int query_socket = -1;
230 int socket4 = -1, socket6 = -1;
232 static char *rcode_strings[] = RCODE_STRINGS;
234 static struct query_mininfo *timeout_queries;
237 * get_uint16:
238 * Get an unsigned short integer from a buffer (in network order)
240 static unsigned short
241 get_uint16(unsigned char *buf) {
242 unsigned short ret;
244 ret = buf[0] * 256 + buf[1];
246 return (ret);
250 * show_startup_info:
251 * Show name/version
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");
262 * show_usage:
263 * Print out usage/syntax information
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);
301 * set_datafile:
302 * Set the datafile to read
304 * Return -1 on failure
305 * Return a non-negative integer otherwise
308 set_datafile(char *new_file) {
309 char *dfname_tmp;
311 if ((new_file == NULL) || (new_file[0] == '\0')) {
312 fprintf(stderr, "Error: null datafile name\n");
313 return (-1);
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);
322 free(datafile_name);
323 datafile_name = dfname_tmp;
325 strcpy(datafile_name, new_file);
326 use_stdin = FALSE;
328 return (0);
332 * set_input_stdin:
333 * Set the input to be stdin (instead of a datafile)
335 void
336 set_input_stdin(void) {
337 use_stdin = TRUE;
338 free(datafile_name);
339 datafile_name = NULL;
343 * set_server:
344 * Set the server to be queried
346 * Return -1 on failure
347 * Return a non-negative integer otherwise
350 set_server(char *new_name) {
351 static struct hostent *server_he;
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);
358 if ((new_name == NULL) || (new_name[0] == '\0')) {
359 fprintf(stderr, "Error: null server name\n");
360 return (-1);
363 free(server_to_query);
364 server_to_query = NULL;
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);
372 strcpy(server_to_query, new_name);
374 return (0);
378 * set_server_port:
379 * Set the port on which to contact the server
381 * Return -1 if port is invalid
382 * Return a non-negative integer otherwise
385 set_server_port(char *new_port) {
386 unsigned int uint_val;
388 if ((is_uint(new_port, &uint_val)) != TRUE)
389 return (-1);
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);
398 free(server_port);
399 server_port = NULL;
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);
408 strcpy(server_port, new_port);
410 return (0);
415 set_server_sa(void) {
416 struct addrinfo hints, *res;
417 static struct protoent *proto;
418 int error;
420 if (proto == NULL && (proto = getprotobyname("udp")) == NULL) {
421 fprintf(stderr, "Error: getprotobyname call failed");
422 return (-1);
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);
436 /* replace the server's addrinfo */
437 if (server_ai != NULL)
438 freeaddrinfo(server_ai);
439 server_ai = res;
440 return (0);
444 * is_digit:
445 * Tests if a character is a digit
447 * Return TRUE if it is
448 * Return FALSE if it is not
451 is_digit(char d) {
452 if (d < '0' || d > '9')
453 return (FALSE);
454 else
455 return (TRUE);
459 * is_uint:
460 * Tests if a string, test_int, is a valid unsigned integer
462 * Sets *result to be the unsigned integer if it is valid
464 * Return TRUE if it is
465 * Return FALSE if it is not
468 is_uint(char *test_int, unsigned int *result) {
469 unsigned long int value;
470 char *end;
472 if (test_int == NULL)
473 return (FALSE);
475 if (is_digit(test_int[0]) == FALSE)
476 return (FALSE);
478 value = strtoul(test_int, &end, 10);
480 if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX))
481 return (FALSE);
483 *result = (unsigned int)value;
484 return (TRUE);
488 * set_max_queries:
489 * Set the maximum number of outstanding queries
491 * Returns -1 on failure
492 * Returns a non-negative integer otherwise
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;
500 if (new_max > query_status_allocated) {
501 temp_stat = realloc(status, new_max * size_qs);
503 if (temp_stat == NULL) {
504 fprintf(stderr, "Error resizing query_status\n");
505 return (-1);
506 } else {
507 status = temp_stat;
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.
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;
524 query_status_allocated = new_max;
528 max_queries_outstanding = new_max;
530 return (0);
534 * parse_args:
535 * Parse program arguments and set configuration options
537 * Return -1 on failure
538 * Return a non-negative integer otherwise
541 parse_args(int argc, char **argv) {
542 int c;
543 unsigned int uint_arg_val;
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);
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);
573 break;
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);
585 break;
587 case 'n':
588 ignore_config_changes = TRUE;
589 break;
591 case 'd':
592 if (set_datafile(optarg) == -1) {
593 fprintf(stderr, "Error setting datafile "
594 "name: %s\n", optarg);
595 return (-1);
597 break;
599 case 's':
600 if (set_server(optarg) == -1) {
601 fprintf(stderr, "Error setting server "
602 "name: %s\n", optarg);
603 return (-1);
605 serverset = TRUE;
606 break;
608 case 'p':
609 if (is_uint(optarg, &uint_arg_val) == TRUE &&
610 uint_arg_val < MAX_PORT)
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);
620 break;
622 case '1':
623 run_only_once = TRUE;
624 break;
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);
636 break;
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);
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);
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);
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);
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);
702 break;
703 case 'h':
704 return (-1);
705 default:
706 fprintf(stderr, "Invalid option: -%c\n", optopt);
707 return (-1);
711 if (run_only_once == FALSE && use_timelimit == FALSE)
712 run_only_once = TRUE;
714 return (0);
718 * open_datafile:
719 * Open the data file ready for reading
721 * Return -1 on failure
722 * Return non-negative integer on success
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);
740 * close_datafile:
741 * Close the data file if any is open
743 * Return -1 on failure
744 * Return non-negative integer on success, including if not needed
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);
755 return (0);
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.
763 * Return -1 on failure
764 * Return the socket identifier
767 open_socket(void) {
768 int sock;
769 int ret;
770 int bufsize;
771 struct addrinfo hints, *res;
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;
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);
786 if ((sock = socket(res->ai_family, SOCK_DGRAM,
787 res->ai_protocol)) == -1) {
788 fprintf(stderr, "Error: socket call failed");
789 goto fail;
792 #if defined(AF_INET6) && defined(IPV6_V6ONLY)
793 if (res->ai_family == AF_INET6) {
794 int on = 1;
796 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
797 &on, sizeof(on)) == -1) {
798 fprintf(stderr,
799 "Warning: setsockopt(IPV6_V6ONLY) failed\n");
802 #endif
804 if (bind(sock, res->ai_addr, res->ai_addrlen) == -1)
805 fprintf(stderr, "Error: bind call failed");
807 freeaddrinfo(res);
809 bufsize = 1024 * socket_bufsize;
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");
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");
821 return (sock);
823 fail:
824 if (res)
825 freeaddrinfo(res);
826 return (-1);
830 * close_socket:
831 * Close the query socket(s)
833 * Return -1 on failure
834 * Return a non-negative integer otherwise
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);
846 if (socket6 != -1) {
847 if (close(socket6) != 0) {
848 fprintf(stderr,
849 "Error: unable to close IPv6 socket\n");
850 return (-1);
854 query_socket = -1;
856 return (0);
860 * change_socket:
861 * Choose an appropriate socket according to the address family of the
862 * current server. Open a new socket if necessary.
864 * Return -1 on failure
865 * Return the socket identifier
868 change_socket(void) {
869 int s, *sockp;
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);
886 if (*sockp == -1) {
887 if ((s = open_socket()) == -1)
888 return (-1);
889 *sockp = s;
892 return (*sockp);
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
903 reset_rttarray(int size) {
904 if (rttarray != NULL)
905 free(rttarray);
906 if (rttarray_interval != NULL)
907 free(rttarray_interval);
909 rttarray = NULL;
910 rttarray_interval = NULL;
911 rtt_max = -1;
912 rtt_min = -1;
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);
921 memset(rttarray, 0, size * sizeof(rttarray[0]));
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);
931 memset(rttarray_interval, 0,
932 size * sizeof(rttarray_interval[0]));
935 return (0);
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
945 set_query_interval(unsigned int qps) {
946 if (qps == 0)
947 return (0);
949 query_interval = (1.0 / (double)qps);
951 return (0);
955 * setup:
956 * Set configuration options from command line arguments
957 * Open datafile ready for reading
959 * Return -1 on failure
960 * Return non-negative integer on success
963 setup(int argc, char **argv) {
964 set_input_stdin();
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);
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);
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);
984 if (parse_args(argc, argv) == -1) {
985 show_usage();
986 return (-1);
989 if (open_datafile() == -1)
990 return (-1);
992 if (set_server_sa() == -1)
993 return (-1);
995 if ((query_socket = change_socket()) == -1)
996 return (-1);
998 if (reset_rttarray(rttarray_size) == -1)
999 return (-1);
1001 if (set_query_interval(target_qps) == -1)
1002 return (-1);
1004 return (0);
1008 * set_timenow:
1009 * Set a timeval struct to indicate the current time
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;
1022 * addtv:
1023 * add tv1 and tv2, store the result in tv_result.
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;
1036 * difftv:
1037 * Find the difference in seconds between two timeval structs.
1039 * Return the difference between tv1 and tv2 in seconds in a double.
1041 double
1042 difftv(struct timeval tv1, struct timeval tv2) {
1043 long diff_sec, diff_usec;
1044 double diff;
1046 diff_sec = tv1.tv_sec - tv2.tv_sec;
1047 diff_usec = tv1.tv_usec - tv2.tv_usec;
1049 diff = (double)diff_sec + ((double)diff_usec / 1000000.0);
1051 return (diff);
1055 * timelimit_reached:
1056 * Have we reached the time limit (if any)?
1058 * Returns FALSE if there is no time limit or if we have not reached it
1059 * Returns TRUE otherwise
1062 timelimit_reached(void) {
1063 struct timeval time_now;
1065 set_timenow(&time_now);
1067 if (use_timelimit == FALSE)
1068 return (FALSE);
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);
1086 * keep_sending:
1087 * Should we keep sending queries or stop here?
1089 * Return TRUE if we should keep on sending queries
1090 * Return FALSE if we should stop
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).
1098 keep_sending(int *reached_end_input) {
1099 static int stop = FALSE;
1101 if (stop == TRUE)
1102 return (FALSE);
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);
1122 * queries_outstanding:
1123 * How many queries are outstanding?
1125 * Returns the number of outstanding queries
1127 unsigned int
1128 queries_outstanding(void) {
1129 return (num_queries_outstanding);
1133 * next_input_line:
1134 * Get the next non-comment line from the input file
1136 * Put text in line, up to max of n chars. Skip comment lines.
1137 * Skip empty lines.
1139 * Return line length on success
1140 * Return 0 if cannot read a non-comment line (EOF or error)
1143 next_input_line(char *line, int n) {
1144 char *result;
1146 do {
1147 result = fgets(line, n, datafile_ptr);
1148 } while ((result != NULL) &&
1149 ((line[0] == COMMENT_CHAR) || (line[0] == '\n')));
1151 if (result == NULL)
1152 return (0);
1153 else
1154 return (strlen(line));
1158 * identify_directive:
1159 * Gives us a numerical value equivelant for a directive string
1161 * Returns the value for the directive
1162 * Returns -1 if not a valid directive
1165 identify_directive(char *dir) {
1166 static char *directives[] = DIRECTIVES;
1167 static int dir_values[] = DIR_VALUES;
1168 unsigned int index, num_directives;
1170 num_directives = sizeof(directives) / sizeof(directives[0]);
1172 if (num_directives > (sizeof(dir_values) / sizeof(int)))
1173 num_directives = sizeof(dir_values) / sizeof(int);
1175 for (index = 0; index < num_directives; index++) {
1176 if (strcmp(dir, directives[index]) == 0)
1177 return (dir_values[index]);
1180 return (-1);
1184 * update_config:
1185 * Update configuration options from a line from the input file
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;
1196 if (ignore_config_changes == TRUE) {
1197 fprintf(stderr, "Ignoring configuration change: %s",
1198 config_change_desc);
1199 return;
1202 strcpy(conf_copy, config_change_desc);
1204 ++config_change_desc;
1206 if (*config_change_desc == '\0') {
1207 fprintf(stderr, "Invalid config: No directive present: %s\n",
1208 conf_copy);
1209 return;
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;
1218 directive = strtok(config_change_desc, WHITESPACE);
1219 config_value = strtok(NULL, WHITESPACE);
1220 trailing_garbage = strtok(NULL, WHITESPACE);
1222 if ((directive_number = identify_directive(directive)) == -1) {
1223 fprintf(stderr, "Invalid config: Bad directive: %s\n",
1224 conf_copy);
1225 return;
1228 if (config_value == NULL) {
1229 fprintf(stderr, "Invalid config: No value present: %s\n",
1230 conf_copy);
1231 return;
1234 if (trailing_garbage != NULL) {
1235 fprintf(stderr, "Config warning: "
1236 "trailing garbage: %s\n", conf_copy);
1239 switch(directive_number) {
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;
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;
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;
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);
1271 break;
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;
1280 check = is_uint(config_value, &uint_val);
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;
1293 } else
1294 fprintf(stderr, "Invalid config: Bad value for "
1295 "%s: %s\n", directive, config_value);
1296 break;
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;
1305 check = is_uint(config_value, &uint_val);
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;
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;
1321 check = is_uint(config_value, &uint_val);
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;
1330 default:
1331 fprintf(stderr, "Invalid config: Bad directive: %s\n",
1332 directive);
1333 break;
1338 * parse_query:
1339 * Parse a query line from the input file
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
1344 * Return -1 on failure
1345 * Return a non-negative integer otherwise
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;
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);
1360 strcpy(incopy, input);
1362 domain_str = strtok(incopy, WHITESPACE);
1363 type_str = strtok(NULL, WHITESPACE);
1365 if ((domain_str == NULL) || (type_str == NULL)) {
1366 fprintf(stderr, "Invalid query input format: %s\n", input);
1367 return (-1);
1370 if (strlen(domain_str) > qnlen) {
1371 fprintf(stderr, "Query domain too long: %s\n", domain_str);
1372 return (-1);
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;
1382 if (found == FALSE) {
1383 fprintf(stderr, "Query type not understood: %s\n", type_str);
1384 return (-1);
1387 strcpy(qname, domain_str);
1389 return (0);
1393 * dispatch_query:
1394 * Send the query packet for the entry
1396 * Return -1 on failure
1397 * Return a non-negative integer otherwise
1400 dispatch_query(unsigned short int id, char *dom, int qt, u_char **pktp,
1401 int *pktlenp)
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;
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);
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);
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;
1443 packet_buffer[0] = id_ptr[0];
1444 packet_buffer[1] = id_ptr[1];
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);
1454 if (bytes_sent != buffer_len)
1455 fprintf(stderr, "Warning: incomplete packet sent: %s %d\n",
1456 dom, qt);
1458 *pktp = packet_buffer;
1459 *pktlenp = buffer_len;
1461 return (0);
1465 * send_query:
1466 * Send a query based on a line of input
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;
1478 use_query_id++;
1480 if (parse_query(query_desc, domain, qname_len, &query_type) == -1) {
1481 fprintf(stderr, "Error parsing query: %s\n", query_desc);
1482 return;
1485 if (dispatch_query(use_query_id, domain, query_type,
1486 &qpkt, &qpkt_len) == -1) {
1487 char *addrstr;
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;
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;
1511 printf("[Status] Sending queries (beginning with %s)\n",
1512 serveraddr);
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++);
1519 if (status[count].in_use == TRUE) {
1520 fprintf(stderr, "Unexpected error: We have run out of "
1521 "status[] space!\n");
1522 return;
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;
1537 status[count].in_use = TRUE;
1539 if (num_queries_sent_interval == 0)
1540 set_timenow(&time_of_first_query_interval);
1542 num_queries_sent++;
1543 num_queries_sent_interval++;
1544 num_queries_outstanding++;
1547 void
1548 register_rtt(struct timeval *timestamp, char *qname, int qtype,
1549 unsigned int rcode)
1551 int i;
1552 int oldquery = FALSE;
1553 struct timeval now;
1554 double rtt;
1556 set_timenow(&now);
1557 rtt = difftv(now, *timestamp);
1559 if (difftv(*timestamp, time_of_first_query_interval) < 0)
1560 oldquery = TRUE;
1562 if (rtt_max < 0 || rtt_max < rtt)
1563 rtt_max = rtt;
1565 if (rtt_min < 0 || rtt_min > rtt)
1566 rtt_min = rtt;
1568 rtt_total += rtt;
1569 rtt_counted++;
1571 if (!oldquery) {
1572 if (rtt_max_interval < 0 || rtt_max_interval < rtt)
1573 rtt_max_interval = rtt;
1575 if (rtt_min_interval < 0 || rtt_min_interval > rtt)
1576 rtt_min_interval = rtt;
1578 rtt_total_interval += rtt;
1579 rtt_counted_interval++;
1582 if (rttarray == NULL)
1583 return;
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++;
1600 * register_response:
1601 * Register receipt of a query
1603 * Removes (sets in_use = FALSE) the record for the given query id in
1604 * status[] if any exists.
1606 void
1607 register_response(unsigned short int id, unsigned int rcode, char *qname,
1608 int qtype)
1610 unsigned int ct = 0;
1611 int found = FALSE;
1612 struct timeval now;
1613 double rtt;
1615 if (timeout_queries != NULL) {
1616 struct query_mininfo *qi = &timeout_queries[id];
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;
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;
1633 register_rtt(&status[ct].sent_timestamp, qname, qtype,
1634 rcode);
1636 if (status[ct].desc) {
1637 printf("> %s %s\n", rcode_strings[rcode],
1638 status[ct].desc);
1639 free(status[ct].desc);
1644 if (countrcodes && (found == TRUE || target_qps > 0))
1645 rcodecounts[rcode]++;
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);
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.
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;
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);
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;
1684 if (numbytes < DNS_HEADERLEN) {
1685 if (verbose)
1686 fprintf(stderr, "Malformed response\n");
1687 return;
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;
1699 if (numbytes < DNS_HEADERLEN + qnamelen + 2) {
1700 if (verbose)
1701 fprintf(stderr, "Malformed response\n");
1702 return;
1704 qtype = get_uint16(in_buf + DNS_HEADERLEN + qnamelen);
1706 register_response(resp_id, flags & 0xF, qname, qtype);
1710 * data_available:
1711 * Is there data available on the given file descriptor?
1713 * Return TRUE if there is
1714 * Return FALSE otherwise
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;
1724 /* Set list of file descriptors */
1725 FD_ZERO(&read_fds);
1726 if (socket4 != -1) {
1727 FD_SET(socket4, &read_fds);
1728 maxfd = socket4;
1730 if (socket6 != -1) {
1731 FD_SET(socket6, &read_fds);
1732 if (maxfd == -1 || maxfd < socket6)
1733 maxfd = socket6;
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;
1744 retval = select(maxfd + 1, &read_fds, NULL, NULL, &tv);
1746 if (socket4 != -1 && FD_ISSET(socket4, &read_fds)) {
1747 available = TRUE;
1748 process_single_response(socket4);
1750 if (socket6 != -1 && FD_ISSET(socket6, &read_fds)) {
1751 available = TRUE;
1752 process_single_response(socket6);
1755 return (available);
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.
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();
1771 if (adjust_rate == TRUE) {
1772 double u;
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));
1780 * Wait until a response arrives or the specified limit is
1781 * reached.
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;
1792 * We have reached the limit. Read as many responses
1793 * as possible without waiting, and exit.
1795 if (wait == 0) {
1796 while (data_available(0.0) == TRUE)
1798 break;
1801 } else {
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.
1807 if ((outstanding == 0) ||
1808 (outstanding < max_queries_outstanding)) {
1809 first_packet_wait = 0.0;
1812 if (data_available(first_packet_wait) == TRUE) {
1813 while (data_available(0.0) == TRUE)
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.
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;
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.
1837 if (target_qps > 0 && sending == TRUE && count == 0 &&
1838 queries_outstanding() == max_queries_outstanding) {
1839 struct timeval next, now;
1840 double n;
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));
1847 set_timenow(&now);
1848 if (difftv(next, now) <= 0) {
1849 timeout_reduced = TRUE;
1850 timeout = 0.001; /* XXX: ad-hoc value */
1854 set_timenow(&curr_time);
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))
1861 status[count].in_use = FALSE;
1862 num_queries_outstanding--;
1864 if (timeout_queries != NULL) {
1865 struct query_mininfo *qi;
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++;
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++;
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);
1897 * print_histogram
1898 * Print RTT histogram to the specified file in the gnuplot format
1900 void
1901 print_histogram(unsigned int total) {
1902 int i;
1903 double ratio;
1904 FILE *fp;
1906 if (rtt_histogram_file == NULL || rttarray == NULL)
1907 return;
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;
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);
1924 (void)fclose(fp);
1928 * print_statistics:
1929 * Print out statistics based on the results of the test
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)
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;
1947 num_queries_completed = sent - timed_out;
1949 if (num_queries_completed == 0) {
1950 per_lost = 0.0;
1951 per_completed = 0.0;
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;
1959 per_lost2 = (100.0 * (double)(timed_out - possibly_delayed))
1960 / (double)sent;
1961 per_completed2 = 100 - per_lost2;
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;
1979 queries_per_sec_total = (double)sent /
1980 difftv(*end_query, *first_query);
1983 if (rcounted > 0) {
1984 int i;
1985 double sum = 0;
1987 rtt_average = rtotal / (double)rcounted;
1988 for (i = 0; i < rttarray_size; i++) {
1989 if (rarray[i] != 0) {
1990 double mean, diff;
1992 mean = (double)(i * rttarray_unit) +
1993 (double)rttarray_unit / 2;
1994 diff = rtt_average - (mean / 1000000.0);
1995 sum += (diff * diff) * rarray[i];
1998 rtt_stddev = sqrt(sum / (double)rcounted);
1999 } else {
2000 rtt_average = 0.0;
2001 rtt_stddev = 0.0;
2004 printf("\n");
2006 printf("%sStatistics:\n", intermediate ? "Intermediate " : "");
2008 printf("\n");
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"));
2024 printf("\n");
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);
2032 printf("\n");
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);
2040 if (!intermediate) /* XXX should we print this case also? */
2041 print_histogram(num_queries_completed);
2043 printf("\n");
2045 if (countrcodes) {
2046 unsigned int i;
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]);
2054 printf("\n");
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);
2064 printf("\n");
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);
2072 printf("\n");
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);
2079 if (target_qps > 0) {
2080 printf(" Total QPS/target: %.6lf/%d qps\n",
2081 queries_per_sec_total, target_qps);
2084 printf("\n");
2087 void
2088 print_interval_statistics() {
2089 struct timeval time_now;
2091 if (use_timelimit == FALSE)
2092 return;
2094 if (setup_phase == TRUE)
2095 return;
2097 if (print_interval == 0)
2098 return;
2100 if (timelimit_reached() == TRUE)
2101 return;
2103 set_timenow(&time_now);
2104 if (difftv(time_now, time_of_first_query_interval)
2105 <= (double)print_interval)
2106 return;
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);
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);
2135 * queryperf Program Mainline
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];
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;
2153 input_line[0] = '\0';
2155 show_startup_info();
2157 if (setup(argc, argv) == -1)
2158 return (-1);
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;
2172 printf("[Status] Processing input data\n");
2174 while ((sending = keep_sending(&got_eof)) == TRUE ||
2175 queries_outstanding() > 0)
2177 if (num_queries_sent_interval > 0){
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
2183 print_interval_statistics();
2185 adjust_rate = FALSE;
2187 while ((sending = keep_sending(&got_eof)) == TRUE &&
2188 queries_outstanding() < max_queries_outstanding)
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';
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! :)
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;
2220 process_responses(adjust_rate);
2221 retire_old_queries(sending);
2224 set_timenow(&time_of_end_of_run);
2226 printf("[Status] Testing complete\n");
2228 close_socket();
2229 close_datafile();
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);
2238 return (0);