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