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