1 /*	$NetBSD: perftcpdns.c,v 1.3 2014/12/10 04:37:56 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2013, 2014  Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * TCP DNS perf tool
21  *
22  * main parameters are -r<rate> and <server>
23  * standard options are 4|6 (IPv4|IPv6), rate computations, terminaisons,
24  * EDNS0, NOERROR|NXDOMAIN, template (for your own query), diags,
25  * alternate server port and UDP version.
26  *
27  * To help to crush kernels (unfortunately both client and server :-)
28  * this version of the tool is multi-threaded:
29  *  - the master thread inits, monitors the activity each millisecond,
30  *   and report results when finished
31  *  - the connecting thread computes the date of the next connection,
32  *   creates a socket, makes it non blocking, binds it if wanted,
33  *   connects it and pushes it on the output epoll queue
34  *  - the sending thread gets by epoll connected sockets, timeouts
35  *   embryonic connections, sends queries and pushes sockets on
36  *   the input epoll queue
37  *  - the receiving thread gets by epoll sockets with a pending
38  *   response, receives responses, timeouts unanswered queries,
39  *   and recycles (by closing them) all sockets.
40  *
41  * Rate computation details:
42  *  - the target rate is in query+response per second.
43  *  - rating is done by the connecting thread.
44  *  - of course the tool is always late so the target rate is never
45  *   reached. BTW there is no attempt to internally adjust the
46  *   effective rate to the target one: this must be by tuning
47  *   the rate related parameters, first the -r<rate> itself.
48  *  - at the beginning of the connecting thread iteration loop
49  *   (second "loops" counter) the date of the due (aka next) connect()
50  *   call is computed from the last one with 101% of the rate.
51  *  - the due date is compared with the current date (aka now).
52  *  - if the due is before, lateconn counter is incremented, else
53  *   the thread sleeps for the difference,
54  *  - the next step is to reget the current date, if it is still
55  *   before the due date (e.g., because the sleep was interrupted)
56  *   the first shortwait counter is incremented.
57  *  - if it is after (common case) the number of connect calls is
58  *   computed from the difference between now and due divided by rate,
59  *   rounded to the next number,
60  *  - this number of connect() calls is bounded by the -a<aggressiveness>
61  *   parameter to avoid too many back to back new connection attempts.
62  *  - the compconn counter is incremented, errors (other than EINPROGRESS
63  *   from not blocking connect()) are printed. When an error is
64  *   related to a local limit (e.g., EMFILE, EADDRNOTAVAIL or the
65  *   internal ENOMEM) the locallimit counter is incremented.
66  */
67 
68 #ifdef __linux__
69 #define _GNU_SOURCE
70 #endif
71 
72 #include <sys/types.h>
73 #include <sys/epoll.h>
74 #include <sys/prctl.h>
75 #include <sys/select.h>
76 #include <sys/socket.h>
77 #include <sys/wait.h>
78 
79 #include <netinet/in.h>
80 #include <arpa/inet.h>
81 
82 #include <ctype.h>
83 #include <errno.h>
84 #include <fcntl.h>
85 #include <math.h>
86 #include <netdb.h>
87 #include <pthread.h>
88 #include <signal.h>
89 #include <stdint.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <time.h>
94 #include <unistd.h>
95 
96 /* DNS defines */
97 
98 #define NS_TYPE_A		1
99 #define NS_TYPE_NS		2
100 #define NS_TYPE_CNAME		5
101 #define NS_TYPE_SOA		6
102 #define NS_TYPE_NULL		10
103 #define NS_TYPE_PTR		12
104 #define NS_TYPE_MX		15
105 #define NS_TYPE_TXT		16
106 #define NS_TYPE_AAAA		28
107 #define NS_TYPE_OPT		41
108 #define NS_TYPE_DS		43
109 #define NS_TYPE_RRSIG		46
110 #define NS_TYPE_NSEC		47
111 #define NS_TYPE_DNSKEY		48
112 #define NS_TYPE_NSEC3		50
113 #define NS_TYPE_NSEC3PARAM	51
114 #define NS_TYPE_TSIG		250
115 #define NS_TYPE_IXFR		251
116 #define NS_TYPE_AXFR		252
117 #define NS_TYPE_ANY		255
118 
119 #define NS_CLASS_IN		1
120 #define NS_CLASS_ANY		255
121 
122 #define NS_OFF_ID		0
123 #define NS_OFF_FLAGS		2
124 #define NS_OFF_QDCOUNT		4
125 #define NS_OFF_ANCOUNT		6
126 #define NS_OFF_NSCOUNT		8
127 #define NS_OFF_ARCOUNT		10
128 #define NS_OFF_QUESTION		12
129 
130 #define NS_FLAG_QR		0x8000U
131 #define NS_FLAG_AA		0x0400U
132 #define NS_FLAG_TC		0x0200U
133 #define NS_FLAG_RD		0x0100U
134 #define NS_FLAG_RA		0x0080U
135 #define NS_FLAG_AD		0x0020U
136 #define NS_FLAG_CD		0x0010U
137 
138 #define NS_XFLAG_DO		0x8000U
139 
140 #define NS_OPCODE_MASK		0x7000U
141 #define NS_OPCODE_QUERY		0
142 
143 #define NS_RCODE_MASK		0x000fU
144 #define NS_RCODE_NOERROR	0
145 #define NS_RCODE_FORMERR	1
146 #define NS_RCODE_SERVFAIL	2
147 #define NS_RCODE_NXDOMAIN	3
148 #define NS_RCODE_NOIMP		4
149 #define NS_RCODE_REFUSED	5
150 #define NS_RCODE_LAST		6
151 
152 /* chaining macros */
153 
154 #define ISC_INIT(head, headl)		do { \
155 	(head) = -1; \
156 	(headl) = &(head); \
157 } while (/*CONSTCOND*/0)
158 
159 #define ISC_INSERT(head, headl, elm)	do { \
160 	(elm)->next = -1; \
161 	(elm)->prev = (headl); \
162 	*(headl) = (elm) - xlist; \
163 	(headl) = &((elm)->next); \
164 } while (/*CONSTCOND*/0)
165 
166 #define ISC_REMOVE(headl, elm)		do { \
167 	if ((elm)->next != -1) \
168 		xlist[(elm)->next].prev = (elm)->prev; \
169 	else \
170 		(headl) = (elm)->prev; \
171 	*(elm)->prev = (elm)->next; \
172 } while (/*CONSTCOND*/0)
173 
174 /*
175  * Data structures
176  */
177 
178 /*
179  * exchange:
180  *    - per exchange values:
181  *	* order (for debugging)
182  *	* id
183  *	* random (for debugging)
184  *	* time-stamps
185  *
186  * sent/rcvd chain, "next to be received" on entry cache.
187  */
188 
189 struct exchange {				/* per exchange structure */
190 	int sock;				/* socket descriptor */
191 	int next, *prev;			/* chaining */
192 #define X_FREE	0
193 #define X_CONN	1
194 #define X_READY	2
195 #define X_SENT	3
196 	int state;				/* state */
197 	uint16_t id;				/* ID */
198 	uint64_t order;				/* number of this exchange */
199 	struct timespec ts0, ts1, ts2, ts3;	/* timespecs */
200 };
201 struct exchange *xlist;				/* exchange list */
202 int xlast;					/* number of exchanges */
203 int xconn, *xconnl;				/* connecting list */
204 int xready, *xreadyl;				/* connected list */
205 int xsent, *xsentl;				/* sent list */
206 int xfree, *xfreel;				/* free list */
207 int xused;					/* next to be used list */
208 pthread_mutex_t mtxconn, mtxsent, mtxfree;	/* mutexes */
209 uint64_t xccount;				/* connected counters */
210 uint64_t xscount;				/* sent counters */
211 uint64_t xrcount;				/* received counters */
212 
213 /*
214  * statictics counters and accumulators
215  */
216 
217 uint64_t recverr, tooshort, locallimit;		/* error counters */
218 uint64_t loops[4], shortwait[3];		/* rate stats */
219 uint64_t lateconn, compconn;			/* rate stats (cont) */
220 uint64_t badconn, collconn, badsent, collsent;	/* rate stats (cont) */
221 uint64_t badid, notresp;			/* bad response counters */
222 uint64_t rcodes[NS_RCODE_LAST + 1];		/* rcode counters */
223 double dmin = 999999999.;			/* minimum delay */
224 double dmax = 0.;				/* maximum delay */
225 double dsum = 0.;				/* delay sum */
226 double dsumsq = 0.;				/* square delay sum */
227 
228 /*
229  * command line parameters
230  */
231 
232 int edns0;				/* EDNS0 DO flag */
233 int ipversion = 0;			/* IP version */
234 int rate;				/* rate in connections per second */
235 int noreport;				/* disable auto reporting */
236 int report;				/* delay between two reports */
237 uint32_t range;				/* randomization range */
238 uint32_t maxrandom;			/* maximum random value */
239 int basecnt;				/* base count */
240 char *base[2];				/* bases */
241 int gotnumreq = -1;			/* numreq[0] was set */
242 int numreq[2];				/* number of exchanges */
243 int period;				/* test period */
244 int gotlosttime = -1;			/* losttime[0] was set */
245 double losttime[2] = {.5, 1.};		/* delay for a timeout  */
246 int gotmaxloss = -1;			/* max{p}loss[0] was set */
247 int maxloss[2];				/* maximum number of losses */
248 double maxploss[2] = {0., 0.};		/* maximum percentage */
249 char *localname;			/* local address or interface */
250 int aggressiveness = 1;			/* back to back connections */
251 int seeded;				/* is a seed provided */
252 unsigned int seed;			/* randomization seed */
253 char *templatefile;			/* template file name */
254 int rndoffset = -1;			/* template offset (random) */
255 char *diags;				/* diagnostic selectors */
256 char *servername;			/* server */
257 int ixann;				/* ixann NXDOMAIN */
258 int udp;				/* use UDP in place of TCP */
259 int minport, maxport, curport;		/* port range */
260 
261 /*
262  * global variables
263  */
264 
265 struct sockaddr_storage localaddr;	/* local socket address */
266 struct sockaddr_storage serveraddr;	/* server socket address */
267 in_port_t port = 53;			/* server socket port */
268 
269 int epoll_ifd, epoll_ofd;		/* epoll file descriptors */
270 #ifndef EVENTS_CNT
271 #define EVENTS_CNT	16
272 #endif
273 struct epoll_event ievents[EVENTS_CNT];	/* polled input events */
274 struct epoll_event oevents[EVENTS_CNT];	/* polled output events */
275 int interrupted, fatal;			/* to finish flags */
276 
277 uint8_t obuf[4098], ibuf[4098];		/* I/O buffers */
278 char tbuf[4098];			/* template buffer */
279 
280 struct timespec boot;			/* the date of boot */
281 struct timespec last;			/* the date of last connect */
282 struct timespec due;			/* the date of next connect */
283 struct timespec dreport;		/* the date of next reporting */
284 struct timespec finished;		/* the date of finish */
285 
286 /*
287  * template
288  */
289 
290 size_t length_query;
291 uint8_t template_query[4096];
292 size_t random_query;
293 
294 /*
295  * threads
296  */
297 
298 pthread_t master, connector, sender, receiver;
299 
300 /*
301  * initialize data structures handling exchanges
302  */
303 
304 void
inits(void)305 inits(void)
306 {
307 	int idx;
308 
309 	ISC_INIT(xconn, xconnl);
310 	ISC_INIT(xready, xreadyl);
311 	ISC_INIT(xsent, xsentl);
312 	ISC_INIT(xfree, xfreel);
313 
314 	if ((pthread_mutex_init(&mtxconn, NULL) != 0) ||
315 	    (pthread_mutex_init(&mtxsent, NULL) != 0) ||
316 	    (pthread_mutex_init(&mtxfree, NULL) != 0)) {
317 		fprintf(stderr, "pthread_mutex_init failed\n");
318 		exit(1);
319 	}
320 
321 	epoll_ifd = epoll_create(EVENTS_CNT);
322 	if (epoll_ifd < 0) {
323 		perror("epoll_create(input)");
324 		exit(1);
325 	}
326 	epoll_ofd = epoll_create(EVENTS_CNT);
327 	if (epoll_ofd < 0) {
328 		perror("epoll_create(output)");
329 		exit(1);
330 	}
331 
332 	xlist = (struct exchange *) malloc(xlast * sizeof(struct exchange));
333 	if (xlist == NULL) {
334 		perror("malloc(exchanges)");
335 		exit(1);
336 	}
337 	memset(xlist, 0, xlast * sizeof(struct exchange));
338 
339 	for (idx = 0; idx < xlast; idx++)
340 		xlist[idx].sock = xlist[idx].next = -1;
341 }
342 
343 /*
344  * build a TCP DNS QUERY
345  */
346 
347 void
build_template_query(void)348 build_template_query(void)
349 {
350 	uint8_t *p = template_query;
351 	uint16_t v;
352 
353 	/* flags */
354 	p += NS_OFF_FLAGS;
355 	v = NS_FLAG_RD;
356 	*p++ = v >> 8;
357 	*p++ = v & 0xff;
358 	/* qdcount */
359 	v = 1;
360 	*p++ = v >> 8;
361 	*p++ = v & 0xff;
362 	/* ancount */
363 	v = 0;
364 	*p++ = v >> 8;
365 	*p++ = v & 0xff;
366 	/* nscount */
367 	v = 0;
368 	*p++ = v >> 8;
369 	*p++ = v & 0xff;
370 	/* arcount */
371 	v = edns0;
372 	*p++ = v >> 8;
373 	*p++ = v & 0xff;
374 	/* icann.link (or ixann.link) */
375 	*p++ = 5;
376 	*p++ = 'i';
377 	if (ixann == 0)
378 		*p++ = 'c';
379 	else
380 		*p++ = 'x';
381 	*p++ = 'a';
382 	*p++ = 'n';
383 	*p++ = 'n';
384 	*p++ = 4;
385 	*p++ = 'l';
386 	*p++ = 'i';
387 	*p++ = 'n';
388 	*p++ = 'k';
389 	*p++ = 0;
390 	/* type A/AAAA */
391 	if (ipversion == 4)
392 		v = NS_TYPE_A;
393 	else
394 		v = NS_TYPE_AAAA;
395 	*p++ = v >> 8;
396 	*p++ = v & 0xff;
397 	/* class IN */
398 	v = NS_CLASS_IN;
399 	*p++ = v >> 8;
400 	*p++ = v & 0xff;
401 	/* EDNS0 OPT with DO */
402 	if (edns0) {
403 		/* root name */
404 		*p++ = 0;
405 		/* type OPT */
406 		v = NS_TYPE_OPT;
407 		*p++ = v >> 8;
408 		*p++ = v & 0xff;
409 		/* class UDP length */
410 		v = 4096;
411 		*p++ = v >> 8;
412 		*p++ = v & 0xff;
413 		/* extended rcode 0 */
414 		*p++ = 0;
415 		/* version 0 */
416 		*p++ = 0;
417 		/* extended flags DO */
418 		v = NS_XFLAG_DO;
419 		*p++ = v >> 8;
420 		*p++ = v & 0xff;
421 		/* rdlength */
422 		v = 0;
423 		*p++ = v >> 8;
424 		*p++ = v & 0xff;
425 	}
426 	/* length */
427 	length_query = p - template_query;
428 }
429 
430 /*
431  * get a TCP DNS client QUERY template
432  * from the file given in the command line (-T<template-file>)
433  * and rnd offset (-O<random-offset>)
434  */
435 
436 void
get_template_query(void)437 get_template_query(void)
438 {
439 	uint8_t *p = template_query;
440 	int fd, cc, i, j;
441 
442 	fd = open(templatefile, O_RDONLY);
443 	if (fd < 0) {
444 		fprintf(stderr, "open(%s): %s\n",
445 			templatefile, strerror(errno));
446 		exit(2);
447 	}
448 	cc = read(fd, tbuf, sizeof(tbuf));
449 	(void) close(fd);
450 	if (cc < 0) {
451 		fprintf(stderr, "read(%s): %s\n",
452 			templatefile, strerror(errno));
453 		exit(1);
454 	}
455 	if (cc < NS_OFF_QUESTION + 6) {
456 		fprintf(stderr,"file '%s' too small\n", templatefile);
457 		exit(2);
458 	}
459 	if (cc > 4096) {
460 		fprintf(stderr,"file '%s' too large\n", templatefile);
461 		exit(2);
462 	}
463 	j = 0;
464 	for (i = 0; i < cc; i++) {
465 		if (isspace((int) tbuf[i]))
466 			continue;
467 		if (!isxdigit((int) tbuf[i])) {
468 			fprintf(stderr,
469 				"illegal char[%d]='%c' in file '%s'\n",
470 				i, (int) tbuf[i], templatefile);
471 			exit(2);
472 		}
473 		tbuf[j] = tbuf[i];
474 		j++;
475 	}
476 	cc = j;
477 	if ((cc & 1) != 0) {
478 		fprintf(stderr,
479 			"odd number of hexadecimal digits in file '%s'\n",
480 			templatefile);
481 		exit(2);
482 	}
483 	length_query = cc >> 1;
484 	for (i = 0; i < cc; i += 2)
485 		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
486 	if (rndoffset >= 0)
487 		random_query = (size_t) rndoffset;
488 	if (random_query > length_query) {
489 		fprintf(stderr,
490 			"random (at %zu) outside the template (length %zu)?\n",
491 			random_query, length_query);
492 		exit(2);
493 	}
494 }
495 
496 #if 0
497 /*
498  * randomize the value of the given field:
499  *   - offset of the field
500  *   - random seed (used as it when suitable)
501  *   - returns the random value which was used
502  */
503 
504 uint32_t
505 randomize(size_t offset, uint32_t r)
506 {
507 	uint32_t v;
508 
509 	if (range == 0)
510 		return 0;
511 	if (range == UINT32_MAX)
512 		return r;
513 	if (maxrandom != 0)
514 		while (r >= maxrandom)
515 			r = (uint32_t) random();
516 	r %= range + 1;
517 	v = r;
518 	v += obuf[offset];
519 	obuf[offset] = v;
520 	if (v < 256)
521 		return r;
522 	v >>= 8;
523 	v += obuf[offset - 1];
524 	obuf[offset - 1] = v;
525 	if (v < 256)
526 		return r;
527 	v >>= 8;
528 	v += obuf[offset - 2];
529 	obuf[offset - 2] = v;
530 	if (v < 256)
531 		return r;
532 	v >>= 8;
533 	v += obuf[offset - 3];
534 	obuf[offset - 3] = v;
535 	return r;
536 }
537 #endif
538 
539 /*
540  * flush/timeout connect
541  */
542 
543 void
flushconnect(void)544 flushconnect(void)
545 {
546 	struct exchange *x;
547 	struct timespec now;
548 	int idx = xconn;
549 	int cnt = 10;
550 	double waited;
551 
552 	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
553 		perror("clock_gettime(flushconnect)");
554 		fatal = 1;
555 		(void) pthread_kill(master, SIGTERM);
556 		return;
557 	}
558 
559 	while (--cnt >= 0) {
560 		if (idx < 0)
561 			return;
562 		x = xlist + idx;
563 		idx = x->next;
564 		if (x->state != X_CONN)
565 			abort();
566 		/* check for a timed-out connection */
567 		waited = now.tv_sec - x->ts0.tv_sec;
568 		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
569 		if (waited < losttime[0])
570 			return;
571 		/* garbage collect timed-out connections */
572 		if (pthread_mutex_lock(&mtxconn) != 0) {
573 			fprintf(stderr, "pthread_mutex_lock(flushconnect)");
574 			fatal = 1;
575 			(void) pthread_kill(master, SIGTERM);
576 			return;
577 		}
578 		ISC_REMOVE(xconnl, x);
579 		if (pthread_mutex_unlock(&mtxconn) != 0) {
580 			fprintf(stderr, "pthread_mutex_unlock(flushconnect)");
581 			fatal = 1;
582 			(void) pthread_kill(master, SIGTERM);
583 			return;
584 		}
585 		(void) close(x->sock);
586 		x->sock = -1;
587 		collconn++;
588 		if (pthread_mutex_lock(&mtxfree) != 0) {
589 			fprintf(stderr, "pthread_mutex_lock(flushconnect)");
590 			fatal = 1;
591 			(void) pthread_kill(master, SIGTERM);
592 			return;
593 		}
594 		x->state = X_FREE;
595 		ISC_INSERT(xfree, xfreel, x);
596 		if (pthread_mutex_unlock(&mtxfree) != 0) {
597 			fprintf(stderr, "pthread_mutex_unlock(flushconnect)");
598 			fatal = 1;
599 			(void) pthread_kill(master, SIGTERM);
600 			return;
601 		}
602 	}
603 }
604 
605 /*
606  * poll connected
607  */
608 
609 void
pollconnect(int topoll)610 pollconnect(int topoll)
611 {
612 	struct exchange *x;
613 	int evn, idx, err;
614 	socklen_t len = sizeof(int);
615 
616 	for (evn = 0; evn < topoll; evn++) {
617 		idx = oevents[evn].data.fd;
618 		x = xlist + idx;
619 		if (x->state != X_CONN)
620 			continue;
621 		if (oevents[evn].events == 0)
622 			continue;
623 		if (pthread_mutex_lock(&mtxconn) != 0) {
624 			fprintf(stderr, "pthread_mutex_lock(pollconnect)");
625 			fatal = 1;
626 			(void) pthread_kill(master, SIGTERM);
627 			return;
628 		}
629 		ISC_REMOVE(xconnl, x);
630 		if (pthread_mutex_unlock(&mtxconn) != 0) {
631 			fprintf(stderr, "pthread_mutex_unlock(pollconnect)");
632 			fatal = 1;
633 			(void) pthread_kill(master, SIGTERM);
634 			return;
635 		}
636 		oevents[evn].events = 0;
637 		if ((getsockopt(x->sock, SOL_SOCKET, SO_ERROR,
638 				&err, &len) < 0) ||
639 		    (err != 0)) {
640 			(void) close(x->sock);
641 			x->sock = -1;
642 			badconn++;
643 			if (pthread_mutex_lock(&mtxfree) != 0) {
644 				fprintf(stderr,
645 					"pthread_mutex_lock(pollconnect)");
646 				fatal = 1;
647 				(void) pthread_kill(master, SIGTERM);
648 				return;
649 			}
650 			x->state = X_FREE;
651 			ISC_INSERT(xfree, xfreel, x);
652 			if (pthread_mutex_unlock(&mtxfree) != 0) {
653 				fprintf(stderr,
654 					"pthread_mutex_unlock(pollconnect)");
655 				fatal = 1;
656 				(void) pthread_kill(master, SIGTERM);
657 				return;
658 			}
659 			continue;
660 		}
661 		x->state = X_READY;
662 		ISC_INSERT(xready, xreadyl, x);
663 	}
664 }
665 
666 /*
667  * send the TCP DNS QUERY
668  */
669 
670 int
sendquery(struct exchange * x)671 sendquery(struct exchange *x)
672 {
673 	ssize_t ret;
674 	size_t off;
675 
676 	if (udp)
677 		off = 0;
678 	else {
679 		off = 2;
680 		/* message length */
681 		obuf[0] = length_query >> 8;
682 		obuf[1]= length_query & 0xff;
683 	}
684 	/* message from template */
685 	memcpy(obuf + off, template_query, length_query);
686 	/* ID */
687 	memcpy(obuf + off + NS_OFF_ID, &x->id, 2);
688 #if 0
689 	/* random */
690 	if (random_query > 0)
691 		x->rnd = randomize(random_query + off, x->rnd);
692 #endif
693 	/* timestamp */
694 	errno = 0;
695 	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
696 	if (ret < 0) {
697 		perror("clock_gettime(send)");
698 		fatal = 1;
699 		(void) pthread_kill(master, SIGTERM);
700 		return -errno;
701 	}
702 	ret = send(x->sock, obuf, length_query + off, 0);
703 	if ((size_t) ret == length_query + off)
704 		return 0;
705 	return -errno;
706 }
707 
708 /*
709  * poll ready and send
710  */
711 
712 void
pollsend(void)713 pollsend(void)
714 {
715 	struct exchange *x;
716 	int idx = xready;
717 	struct epoll_event ev;
718 
719 	memset(&ev, 0, sizeof(ev));
720 	ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
721 	for (;;) {
722 		if (idx < 0)
723 			return;
724 		x = xlist + idx;
725 		ev.data.fd = idx;
726 		idx = x->next;
727 		ISC_REMOVE(xreadyl, x);
728 		if (sendquery(x) < 0) {
729 			(void) close(x->sock);
730 			x->sock = -1;
731 			badsent++;
732 			if (pthread_mutex_lock(&mtxfree) != 0) {
733 				fprintf(stderr,
734 					"pthread_mutex_lock(pollsend)");
735 				fatal = 1;
736 				(void) pthread_kill(master, SIGTERM);
737 				return;
738 			}
739 			x->state = X_FREE;
740 			ISC_INSERT(xfree, xfreel, x);
741 			if (pthread_mutex_unlock(&mtxfree) != 0) {
742 				fprintf(stderr,
743 					"pthread_mutex_unlock(pollsend)");
744 				fatal = 1;
745 				(void) pthread_kill(master, SIGTERM);
746 				return;
747 			}
748 			continue;
749 		}
750 		xscount++;
751 		if (pthread_mutex_lock(&mtxsent) != 0) {
752 			fprintf(stderr, "pthread_mutex_lock(pollsend)");
753 			fatal = 1;
754 			(void) pthread_kill(master, SIGTERM);
755 			return;
756 		}
757 		x->state = X_SENT;
758 		ISC_INSERT(xsent, xsentl, x);
759 		if (pthread_mutex_unlock(&mtxsent) != 0) {
760 			fprintf(stderr, "pthread_mutex_unlock(pollsend)");
761 			fatal = 1;
762 			(void) pthread_kill(master, SIGTERM);
763 			return;
764 		}
765 		if (epoll_ctl(epoll_ifd, EPOLL_CTL_ADD, x->sock, &ev) < 0) {
766 			perror("epoll_ctl(add input)");
767 			fatal = 1;
768 			(void) pthread_kill(master, SIGTERM);
769 			return;
770 		}
771 	}
772 }
773 
774 /*
775  * receive a TCP DNS RESPONSE
776  */
777 
778 void
receiveresp(struct exchange * x)779 receiveresp(struct exchange *x)
780 {
781 	struct timespec now;
782 	ssize_t cc;
783 	size_t off;
784 	uint16_t v;
785 	double delta;
786 
787 	cc = recv(x->sock, ibuf, sizeof(ibuf), 0);
788 	if (cc < 0) {
789 		if ((errno == EAGAIN) ||
790 		    (errno == EWOULDBLOCK) ||
791 		    (errno == EINTR) ||
792 		    (errno == ECONNRESET)) {
793 			recverr++;
794 			return;
795 		}
796 		perror("recv");
797 		fatal = 1;
798 		(void) pthread_kill(master, SIGTERM);
799 		return;
800 	}
801 	if (udp)
802 		off = 0;
803 	else
804 		off = 2;
805 	/* enforce a reasonable length */
806 	if ((size_t) cc < length_query + off) {
807 		tooshort++;
808 		return;
809 	}
810 	/* must match the ID */
811 	if (memcmp(ibuf + off + NS_OFF_ID, &x->id, 2) != 0) {
812 		badid++;
813 		return;
814 	}
815 	/* must be a response */
816 	memcpy(&v, ibuf + off + NS_OFF_FLAGS, 2);
817 	v = ntohs(v);
818 	if ((v & NS_FLAG_QR) == 0) {
819 		notresp++;
820 		return;
821 	}
822 	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
823 		perror("clock_gettime(receive)");
824 		fatal = 1;
825 		(void) pthread_kill(master, SIGTERM);
826 		return;
827 	}
828 	/* got it: update stats */
829 	xrcount++;
830 	x->ts3 = now;
831 	delta = x->ts3.tv_sec - x->ts2.tv_sec;
832 	delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
833 	if (delta < dmin)
834 		dmin = delta;
835 	if (delta > dmax)
836 		dmax = delta;
837 	dsum += delta;
838 	dsumsq += delta * delta;
839 	v &= NS_RCODE_MASK;
840 	if (v >= NS_RCODE_LAST)
841 		v = NS_RCODE_LAST;
842 	rcodes[v] += 1;
843 }
844 
845 /*
846  * flush/timeout receive
847  */
848 
849 void
flushrecv(void)850 flushrecv(void)
851 {
852 	struct exchange *x;
853 	struct timespec now;
854 	int idx = xsent;
855 	int cnt = 5;
856 	double waited;
857 
858 	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
859 		perror("clock_gettime(receive)");
860 		fatal = 1;
861 		(void) pthread_kill(master, SIGTERM);
862 		return;
863 	}
864 
865 	while (--cnt >= 0) {
866 		if (idx < 0)
867 			return;
868 		x = xlist + idx;
869 		idx = x->next;
870 		if (x->state != X_SENT)
871 			abort();
872 		/* check for a timed-out exchange */
873 		waited = now.tv_sec - x->ts2.tv_sec;
874 		waited += (now.tv_nsec - x->ts2.tv_nsec) / 1e9;
875 		if (waited < losttime[1])
876 			return;
877 		/* garbage collect timed-out exchange */
878 		if (pthread_mutex_lock(&mtxsent) != 0) {
879 			fprintf(stderr, "pthread_mutex_lock(flushrecv)");
880 			fatal = 1;
881 			(void) pthread_kill(master, SIGTERM);
882 			return;
883 		}
884 		ISC_REMOVE(xsentl, x);
885 		if (pthread_mutex_unlock(&mtxsent) != 0) {
886 			fprintf(stderr, "pthread_mutex_unlock(flushrecv)");
887 			fatal = 1;
888 			(void) pthread_kill(master, SIGTERM);
889 			return;
890 		}
891 		(void) close(x->sock);
892 		x->sock = -1;
893 		collsent++;
894 		if (pthread_mutex_lock(&mtxfree) != 0) {
895 			fprintf(stderr, "pthread_mutex_lock(flushrecv)");
896 			fatal = 1;
897 			(void) pthread_kill(master, SIGTERM);
898 			return;
899 		}
900 		x->state = X_FREE;
901 		ISC_INSERT(xfree, xfreel, x);
902 		if (pthread_mutex_unlock(&mtxfree) != 0) {
903 			fprintf(stderr, "pthread_mutex_unlock(flushrecv)");
904 			fatal = 1;
905 			(void) pthread_kill(master, SIGTERM);
906 			return;
907 		}
908 	}
909 }
910 
911 /*
912  * poll receive
913  */
914 
915 void
pollrecv(int topoll)916 pollrecv(int topoll)
917 {
918 	struct exchange *x;
919 	int evn, idx;
920 
921 	for (evn = 0; evn < topoll; evn++) {
922 		idx = ievents[evn].data.fd;
923 		x = xlist + idx;
924 		if (x->state != X_SENT)
925 			continue;
926 		if (ievents[evn].events == 0)
927 			continue;
928 		if (pthread_mutex_lock(&mtxsent) != 0) {
929 			fprintf(stderr, "pthread_mutex_lock(pollrecv)");
930 			fatal = 1;
931 			(void) pthread_kill(master, SIGTERM);
932 			return;
933 		}
934 		ISC_REMOVE(xsentl, x);
935 		if (pthread_mutex_unlock(&mtxsent) != 0) {
936 			fprintf(stderr, "pthread_mutex_unlock(pollrecv)");
937 			fatal = 1;
938 			(void) pthread_kill(master, SIGTERM);
939 			return;
940 		}
941 		receiveresp(x);
942 		ievents[evn].events = 0;
943 		(void) close(x->sock);
944 		x->sock = -1;
945 		if (pthread_mutex_lock(&mtxfree) != 0) {
946 			fprintf(stderr, "pthread_mutex_lock(pollrecv)");
947 			fatal = 1;
948 			(void) pthread_kill(master, SIGTERM);
949 			return;
950 		}
951 		x->state = X_FREE;
952 		ISC_INSERT(xfree, xfreel, x);
953 		if (pthread_mutex_unlock(&mtxfree) != 0) {
954 			fprintf(stderr, "pthread_mutex_unlock(pollrecv)");
955 			fatal = 1;
956 			(void) pthread_kill(master, SIGTERM);
957 			return;
958 		}
959 	}
960 }
961 
962 /*
963  * get the TCP DNS socket descriptor (IPv4)
964  */
965 
966 int
getsock4(void)967 getsock4(void)
968 {
969 	int sock;
970 	int flags;
971 
972 	errno = 0;
973 	if (udp)
974 		sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
975 	else
976 		sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
977 	if (sock < 0)
978 		return -errno;
979 
980 	/* make the socket descriptor not blocking */
981 	flags = fcntl(sock, F_GETFL, 0);
982 	if (flags == -1) {
983 		(void) close(sock);
984 		return -errno;
985 	}
986 	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
987 		(void) close(sock);
988 		return -errno;
989 	}
990 
991 	/* bind if wanted */
992 	if (localname != NULL) {
993 		if (curport) {
994 			struct sockaddr_in *l4;
995 
996 			l4 = (struct sockaddr_in *) &localaddr;
997 			l4->sin_port = htons((uint16_t) curport);
998 			curport++;
999 			if (curport > maxport)
1000 				curport = minport;
1001 		}
1002 		if (bind(sock,
1003 			 (struct sockaddr *) &localaddr,
1004 			 sizeof(struct sockaddr_in)) < 0) {
1005 			(void) close(sock);
1006 			return -errno;
1007 		}
1008 	}
1009 
1010 	/* connect */
1011 	if (connect(sock,
1012 		    (struct sockaddr *) &serveraddr,
1013 		    sizeof(struct sockaddr_in)) < 0) {
1014 		if (errno != EINPROGRESS) {
1015 			(void) close(sock);
1016 			return -errno;
1017 		}
1018 	}
1019 	return sock;
1020 }
1021 
1022 /*
1023  * connect the TCP DNS QUERY (IPv4)
1024  */
1025 
1026 int
connect4(void)1027 connect4(void)
1028 {
1029 	struct exchange *x;
1030 	int ret;
1031 	int idx;
1032 	struct epoll_event ev;
1033 
1034 	ret = clock_gettime(CLOCK_REALTIME, &last);
1035 	if (ret < 0) {
1036 		perror("clock_gettime(connect)");
1037 		fatal = 1;
1038 		(void) pthread_kill(master, SIGTERM);
1039 		return -errno;
1040 	}
1041 
1042 	if (xfree >= 0) {
1043 		idx = xfree;
1044 		x = xlist + idx;
1045 		ret = pthread_mutex_lock(&mtxfree);
1046 		if (ret != 0) {
1047 			fprintf(stderr, "pthread_mutex_lock(connect4)");
1048 			fatal = 1;
1049 			(void) pthread_kill(master, SIGTERM);
1050 			return -ret;
1051 		}
1052 		ISC_REMOVE(xfreel, x);
1053 		ret = pthread_mutex_unlock(&mtxfree);
1054 		if (ret != 0) {
1055 			fprintf(stderr, "pthread_mutex_unlock(connect4)");
1056 			fatal = 1;
1057 			(void) pthread_kill(master, SIGTERM);
1058 			return -ret;
1059 		}
1060 	} else if (xused < xlast) {
1061 		idx = xused;
1062 		x = xlist + idx;
1063 		xused++;
1064 	} else
1065 		return -ENOMEM;
1066 
1067 	if ((x->state != X_FREE) || (x->sock != -1))
1068 		abort();
1069 
1070 	memset(x, 0, sizeof(*x));
1071 	memset(&ev, 0, sizeof(ev));
1072 	x->next = -1;
1073 	x->prev = NULL;
1074 	x->ts0 = last;
1075 	x->sock = getsock4();
1076 	if (x->sock < 0) {
1077 		int result = x->sock;
1078 
1079 		x->sock = -1;
1080 		ret = pthread_mutex_lock(&mtxfree);
1081 		if (ret != 0) {
1082 			fprintf(stderr, "pthread_mutex_lock(connect4)");
1083 			fatal = 1;
1084 			(void) pthread_kill(master, SIGTERM);
1085 			return -ret;
1086 		}
1087 		ISC_INSERT(xfree, xfreel, x);
1088 		ret = pthread_mutex_unlock(&mtxfree);
1089 		if (ret != 0) {
1090 			fprintf(stderr, "pthread_mutex_unlock(connect4)");
1091 			fatal = 1;
1092 			(void) pthread_kill(master, SIGTERM);
1093 			return -ret;
1094 		}
1095 		return result;
1096 	}
1097 	ret = pthread_mutex_lock(&mtxconn);
1098 	if (ret != 0) {
1099 		fprintf(stderr, "pthread_mutex_lock(connect4)");
1100 		fatal = 1;
1101 		(void) pthread_kill(master, SIGTERM);
1102 		return -ret;
1103 	}
1104 	x->state = X_CONN;
1105 	ISC_INSERT(xconn, xconnl, x);
1106 	ret = pthread_mutex_unlock(&mtxconn);
1107 	if (ret != 0) {
1108 		fprintf(stderr, "pthread_mutex_unlock(connect4)");
1109 		fatal = 1;
1110 		(void) pthread_kill(master, SIGTERM);
1111 		return -ret;
1112 	}
1113 	ev.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
1114 	ev.data.fd = idx;
1115 	if (epoll_ctl(epoll_ofd, EPOLL_CTL_ADD, x->sock, &ev) < 0) {
1116 		perror("epoll_ctl(add output)");
1117 		fatal = 1;
1118 		(void) pthread_kill(master, SIGTERM);
1119 		return -errno;
1120 	}
1121 	x->order = xccount++;
1122 	x->id = (uint16_t) random();
1123 #if 0
1124 	if (random_query > 0)
1125 		x->rnd = (uint32_t) random();
1126 #endif
1127 	return idx;
1128 }
1129 
1130 /*
1131  * get the TCP DNS socket descriptor (IPv6)
1132  */
1133 
1134 int
getsock6(void)1135 getsock6(void)
1136 {
1137 	int sock;
1138 	int flags;
1139 
1140 	errno = 0;
1141 	if (udp)
1142 		sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
1143 	else
1144 		sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
1145 	if (sock < 0)
1146 		return -errno;
1147 
1148 	/* make the socket descriptor not blocking */
1149 	flags = fcntl(sock, F_GETFL, 0);
1150 	if (flags == -1) {
1151 		(void) close(sock);
1152 		return -errno;
1153 	}
1154 	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
1155 		(void) close(sock);
1156 		return -errno;
1157 	}
1158 
1159 	/* bind if wanted */
1160 	if (localname != NULL) {
1161 		if (curport) {
1162 			struct sockaddr_in6 *l6;
1163 
1164 			l6 = (struct sockaddr_in6 *) &localaddr;
1165 			l6->sin6_port = htons((uint16_t) curport);
1166 			curport++;
1167 			if (curport > maxport)
1168 				curport = minport;
1169 		}
1170 		if (bind(sock,
1171 			 (struct sockaddr *) &localaddr,
1172 			 sizeof(struct sockaddr_in6)) < 0) {
1173 			(void) close(sock);
1174 			return -errno;
1175 		}
1176 	}
1177 
1178 	/* connect */
1179 	if (connect(sock,
1180 		    (struct sockaddr *) &serveraddr,
1181 		    sizeof(struct sockaddr_in6)) < 0) {
1182 		if (errno != EINPROGRESS) {
1183 			(void) close(sock);
1184 			return -errno;
1185 		}
1186 	}
1187 	return sock;
1188 }
1189 
1190 /*
1191  * connect the TCP DNS QUERY (IPv6)
1192  */
1193 
1194 int
connect6(void)1195 connect6(void)
1196 {
1197 	struct exchange *x;
1198 	int ret;
1199 	int idx;
1200 	struct epoll_event ev;
1201 
1202 	ret = clock_gettime(CLOCK_REALTIME, &last);
1203 	if (ret < 0) {
1204 		perror("clock_gettime(connect)");
1205 		fatal = 1;
1206 		(void) pthread_kill(master, SIGTERM);
1207 		return -errno;
1208 	}
1209 
1210 	if (xfree >= 0) {
1211 		idx = xfree;
1212 		x = xlist + idx;
1213 		ret = pthread_mutex_lock(&mtxfree);
1214 		if (ret != 0) {
1215 			fprintf(stderr, "pthread_mutex_lock(connect6)");
1216 			fatal = 1;
1217 			(void) pthread_kill(master, SIGTERM);
1218 			return -ret;
1219 		}
1220 		ISC_REMOVE(xfreel, x);
1221 		ret = pthread_mutex_unlock(&mtxfree);
1222 		if (ret != 0) {
1223 			fprintf(stderr, "pthread_mutex_unlock(connect6)");
1224 			fatal = 1;
1225 			(void) pthread_kill(master, SIGTERM);
1226 			return -ret;
1227 		}
1228 	} else if (xused < xlast) {
1229 		idx = xused;
1230 		x = xlist + idx;
1231 		xused++;
1232 	} else
1233 		return -ENOMEM;
1234 
1235 	memset(x, 0, sizeof(*x));
1236 	memset(&ev, 0, sizeof(ev));
1237 	x->next = -1;
1238 	x->prev = NULL;
1239 	x->ts0 = last;
1240 	x->sock = getsock6();
1241 	if (x->sock < 0) {
1242 		int result = x->sock;
1243 
1244 		x->sock = -1;
1245 		ret = pthread_mutex_lock(&mtxfree);
1246 		if (ret != 0) {
1247 			fprintf(stderr, "pthread_mutex_lock(connect6)");
1248 			fatal = 1;
1249 			(void) pthread_kill(master, SIGTERM);
1250 			return -ret;
1251 		}
1252 		ISC_INSERT(xfree, xfreel, x);
1253 		ret = pthread_mutex_unlock(&mtxfree);
1254 		if (ret != 0) {
1255 			fprintf(stderr, "pthread_mutex_unlock(connect6)");
1256 			fatal = 1;
1257 			(void) pthread_kill(master, SIGTERM);
1258 			return -ret;
1259 		}
1260 		return result;
1261 	}
1262 	ret = pthread_mutex_lock(&mtxconn);
1263 	if (ret != 0) {
1264 		fprintf(stderr, "pthread_mutex_lock(connect6)");
1265 		fatal = 1;
1266 		(void) pthread_kill(master, SIGTERM);
1267 		return -ret;
1268 	}
1269 	x->state = X_CONN;
1270 	ISC_INSERT(xconn, xconnl, x);
1271 	ret = pthread_mutex_unlock(&mtxconn);
1272 	if (ret != 0) {
1273 		fprintf(stderr, "pthread_mutex_unlock(connect6)");
1274 		fatal = 1;
1275 		(void) pthread_kill(master, SIGTERM);
1276 		return -ret;
1277 	}
1278 	ev.events = EPOLLOUT | EPOLLET | EPOLLONESHOT;
1279 	ev.data.fd = idx;
1280 	if (epoll_ctl(epoll_ofd, EPOLL_CTL_ADD, x->sock, &ev) < 0) {
1281 		perror("epoll_ctl(add output)");
1282 		fatal = 1;
1283 		(void) pthread_kill(master, SIGTERM);
1284 		return -errno;
1285 	}
1286 	x->order = xccount++;
1287 	x->id = (uint16_t) random();
1288 #if 0
1289 	if (random_query > 0)
1290 		x->rnd = (uint32_t) random();
1291 #endif
1292 	return idx;
1293 }
1294 
1295 /*
1296  * connector working routine
1297  */
1298 
1299 void *
connecting(void * dummy)1300 connecting(void *dummy)
1301 {
1302 	struct timespec now, ts;
1303 	int ret;
1304 	int i;
1305 	char name[16];
1306 
1307 	dummy = dummy;
1308 
1309 	/* set conn-name */
1310 	memset(name, 0, sizeof(name));
1311 	ret = prctl(PR_GET_NAME, name, 0, 0, 0);
1312 	if (ret < 0)
1313 		perror("prctl(PR_GET_NAME)");
1314 	else {
1315 		memmove(name + 5, name, 11);
1316 		memcpy(name, "conn-", 5);
1317 		ret = prctl(PR_SET_NAME, name, 0, 0, 0);
1318 		if (ret < 0)
1319 			perror("prctl(PR_SET_NAME");
1320 	}
1321 
1322 	for (;;) {
1323 		if (fatal)
1324 			break;
1325 
1326 		loops[1]++;
1327 
1328 		/* compute the delay for the next connection */
1329 		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
1330 			perror("clock_gettime(connecting)");
1331 			fatal = 1;
1332 			(void) pthread_kill(master, SIGTERM);
1333 			break;
1334 		}
1335 
1336 		due = last;
1337 		if (rate == 1)
1338 			due.tv_sec += 1;
1339 		else
1340 			due.tv_nsec += 1010000000 / rate;
1341 		while (due.tv_nsec >= 1000000000) {
1342 			due.tv_sec += 1;
1343 			due.tv_nsec -= 1000000000;
1344 		}
1345 		ts = due;
1346 		ts.tv_sec -= now.tv_sec;
1347 		ts.tv_nsec -= now.tv_nsec;
1348 		while (ts.tv_nsec < 0) {
1349 			ts.tv_sec -= 1;
1350 			ts.tv_nsec += 1000000000;
1351 		}
1352 		/* the connection was already due? */
1353 		if (ts.tv_sec < 0) {
1354 			ts.tv_sec = ts.tv_nsec = 0;
1355 			lateconn++;
1356 		} else {
1357 			/* wait until */
1358 			ret = clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
1359 			if (ret < 0) {
1360 				if (errno == EINTR)
1361 					continue;
1362 				perror("clock_nanosleep");
1363 				fatal = 1;
1364 				(void) pthread_kill(master, SIGTERM);
1365 				break;
1366 			}
1367 		}
1368 
1369 		/* compute how many connections to open */
1370 		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
1371 			perror("clock_gettime(connecting)");
1372 			fatal = 1;
1373 			(void) pthread_kill(master, SIGTERM);
1374 			break;
1375 		}
1376 
1377 		if ((now.tv_sec > due.tv_sec) ||
1378 		    ((now.tv_sec == due.tv_sec) &&
1379 		     (now.tv_nsec >= due.tv_nsec))) {
1380 			double toconnect;
1381 
1382 			toconnect = (now.tv_nsec - due.tv_nsec) / 1e9;
1383 			toconnect += now.tv_sec - due.tv_sec;
1384 			toconnect *= rate;
1385 			toconnect++;
1386 			if (toconnect > (double) aggressiveness)
1387 				i = aggressiveness;
1388 			else
1389 				i = (int) toconnect;
1390 			compconn += i;
1391 			/* open connections */
1392 			while (i-- > 0) {
1393 				if (ipversion == 4)
1394 					ret = connect4();
1395 				else
1396 					ret = connect6();
1397 				if (ret < 0) {
1398 					if ((ret == -EAGAIN) ||
1399 					    (ret == -EWOULDBLOCK) ||
1400 					    (ret == -ENOBUFS) ||
1401 					    (ret == -ENFILE) ||
1402 					    (ret == -EMFILE) ||
1403 					    (ret == -EADDRNOTAVAIL) ||
1404 					    (ret == -ENOMEM))
1405 						locallimit++;
1406 					fprintf(stderr,
1407 						"connect: %s\n",
1408 						strerror(-ret));
1409 					break;
1410 				}
1411 			}
1412 		} else
1413 			/* there was no connection to open */
1414 			shortwait[0]++;
1415 	}
1416 
1417 	return NULL;
1418 }
1419 
1420 /*
1421  * sender working routine
1422  */
1423 
1424 void *
sending(void * dummy)1425 sending(void *dummy)
1426 {
1427 	int ret;
1428 	int nfds;
1429 	char name[16];
1430 
1431 	dummy = dummy;
1432 
1433 	/* set send-name */
1434 	memset(name, 0, sizeof(name));
1435 	ret = prctl(PR_GET_NAME, name, 0, 0, 0);
1436 	if (ret < 0)
1437 		perror("prctl(PR_GET_NAME)");
1438 	else {
1439 		memmove(name + 5, name, 11);
1440 		memcpy(name, "send-", 5);
1441 		ret = prctl(PR_SET_NAME, name, 0, 0, 0);
1442 		if (ret < 0)
1443 			perror("prctl(PR_SET_NAME");
1444 	}
1445 
1446 	for (;;) {
1447 		if (fatal)
1448 			break;
1449 
1450 		loops[2]++;
1451 
1452 		/* epoll_wait() */
1453 		memset(oevents, 0, sizeof(oevents));
1454 		nfds = epoll_wait(epoll_ofd, oevents, EVENTS_CNT, 1);
1455 		if (nfds < 0) {
1456 			if (errno == EINTR)
1457 				continue;
1458 			perror("epoll_wait(output)");
1459 			fatal = 1;
1460 			(void) pthread_kill(master, SIGTERM);
1461 			break;
1462 		}
1463 
1464 		/* connection(s) to finish */
1465 		if (nfds == 0)
1466 			shortwait[1]++;
1467 		else
1468 			pollconnect(nfds);
1469 		if (fatal)
1470 			break;
1471 		flushconnect();
1472 		if (fatal)
1473 			break;
1474 
1475 		/* packet(s) to send */
1476 		pollsend();
1477 		if (fatal)
1478 			break;
1479 	}
1480 
1481 	return NULL;
1482 }
1483 
1484 /*
1485  * receiver working routine
1486  */
1487 
1488 void *
receiving(void * dummy)1489 receiving(void *dummy)
1490 {
1491 	int ret;
1492 	int nfds;
1493 	char name[16];
1494 
1495 	dummy = dummy;
1496 
1497 	/* set recv-name */
1498 	memset(name, 0, sizeof(name));
1499 	ret = prctl(PR_GET_NAME, name, 0, 0, 0);
1500 	if (ret < 0)
1501 		perror("prctl(PR_GET_NAME)");
1502 	else {
1503 		memmove(name + 5, name, 11);
1504 		memcpy(name, "recv-", 5);
1505 		ret = prctl(PR_SET_NAME, name, 0, 0, 0);
1506 		if (ret < 0)
1507 			perror("prctl(PR_SET_NAME");
1508 	}
1509 
1510 	for (;;) {
1511 		if (fatal)
1512 			break;
1513 
1514 		loops[3]++;
1515 
1516 		/* epoll_wait() */
1517 		memset(ievents, 0, sizeof(ievents));
1518 		nfds = epoll_wait(epoll_ifd, ievents, EVENTS_CNT, 1);
1519 		if (nfds < 0) {
1520 			if (errno == EINTR)
1521 				continue;
1522 			perror("epoll_wait(input)");
1523 			fatal = 1;
1524 			(void) pthread_kill(master, SIGTERM);
1525 			break;
1526 		}
1527 
1528 		/* packet(s) to receive */
1529 		if (nfds == 0)
1530 			shortwait[2]++;
1531 		else
1532 			pollrecv(nfds);
1533 		if (fatal)
1534 			break;
1535 		flushrecv();
1536 		if (fatal)
1537 			break;
1538 	}
1539 
1540 	return NULL;
1541 }
1542 
1543 /*
1544  * get the server socket address from the command line:
1545  *  - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
1546  */
1547 
1548 void
getserveraddr(const int flags)1549 getserveraddr(const int flags)
1550 {
1551 	struct addrinfo hints, *res;
1552 	int ret;
1553 
1554 	memset(&hints, 0, sizeof(hints));
1555 	if (ipversion == 4)
1556 		hints.ai_family = AF_INET;
1557 	else
1558 		hints.ai_family = AF_INET6;
1559 	if (udp) {
1560 		hints.ai_socktype = SOCK_DGRAM;
1561 		hints.ai_protocol = IPPROTO_UDP;
1562 	} else {
1563 		hints.ai_socktype = SOCK_STREAM;
1564 		hints.ai_protocol = IPPROTO_TCP;
1565 	}
1566 	hints.ai_flags = AI_ADDRCONFIG | flags;
1567 
1568 	ret = getaddrinfo(servername, NULL, &hints, &res);
1569 	if (ret != 0) {
1570 		fprintf(stderr, "bad server=%s: %s\n",
1571 			servername, gai_strerror(ret));
1572 		exit(2);
1573 	}
1574 	if (res->ai_next != NULL) {
1575 		fprintf(stderr, "ambiguous server=%s\n", servername);
1576 		exit(2);
1577 	}
1578 	memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
1579 	freeaddrinfo(res);
1580 	if (ipversion == 4)
1581 		((struct sockaddr_in *)&serveraddr)->sin_port = htons(port);
1582 	else
1583 		((struct sockaddr_in6 *)&serveraddr)->sin6_port = htons(port);
1584 }
1585 
1586 /*
1587  * get the local socket address from the command line
1588  */
1589 
1590 void
getlocaladdr(void)1591 getlocaladdr(void)
1592 {
1593 	struct addrinfo hints, *res;
1594 	int ret;
1595 
1596 	memset(&hints, 0, sizeof(hints));
1597 	if (ipversion == 4)
1598 		hints.ai_family = AF_INET;
1599 	else
1600 		hints.ai_family = AF_INET6;
1601 	if (udp) {
1602 		hints.ai_socktype = SOCK_DGRAM;
1603 		hints.ai_protocol = IPPROTO_UDP;
1604 	} else {
1605 		hints.ai_socktype = SOCK_STREAM;
1606 		hints.ai_protocol = IPPROTO_TCP;
1607 	}
1608 	hints.ai_flags = AI_ADDRCONFIG;
1609 
1610 	ret = getaddrinfo(localname, NULL, &hints, &res);
1611 	if (ret != 0) {
1612 		fprintf(stderr,
1613 			"bad -l<local-addr=%s>: %s\n",
1614 			localname,
1615 			gai_strerror(ret));
1616 		exit(2);
1617 	}
1618 	/* refuse multiple addresses */
1619 	if (res->ai_next != NULL) {
1620 		fprintf(stderr,
1621 			"ambiguous -l<local-addr=%s>\n",
1622 			localname);
1623 		exit(2);
1624 	}
1625 	memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
1626 	freeaddrinfo(res);
1627 }
1628 
1629 /*
1630  * intermediate reporting
1631  * (note: an in-transit packet can be reported as lost)
1632  */
1633 
1634 void
reporting(void)1635 reporting(void)
1636 {
1637 	dreport.tv_sec += report;
1638 
1639 	if (xccount != 0) {
1640 		printf("connect: %llu, sent: %llu, received: %llu "
1641 		       "(embryonics: %lld, drops: %lld)",
1642 		       (unsigned long long) xccount,
1643 		       (unsigned long long) xscount,
1644 		       (unsigned long long) xrcount,
1645 		       (long long) (xccount - xscount),
1646 		       (long long) (xscount - xrcount));
1647 		if (xrcount != 0) {
1648 			double avg;
1649 
1650 			avg = dsum / xrcount;
1651 			printf(" average: %.3f ms", avg * 1e3);
1652 		}
1653 	}
1654 	printf("\n");
1655 }
1656 
1657 /*
1658  * SIGCHLD handler
1659  */
1660 
1661 void
reapchild(int sig)1662 reapchild(int sig)
1663 {
1664 	int status;
1665 
1666 	sig = sig;
1667 	while (wait3(&status, WNOHANG, NULL) > 0)
1668 		/* continue */;
1669 }
1670 
1671 /*
1672  * SIGINT handler
1673  */
1674 
1675 void
interrupt(int sig)1676 interrupt(int sig)
1677 {
1678 	sig = sig;
1679 	interrupted = 1;
1680 }
1681 
1682 /*
1683  * SIGTERM handler
1684  */
1685 
1686 void
terminate(int sig)1687 terminate(int sig)
1688 {
1689 	sig = sig;
1690 	fatal = 1;
1691 }
1692 
1693 /*
1694  * '-v' handler
1695  */
1696 
1697 void
version(void)1698 version(void)
1699 {
1700 	fprintf(stderr, "version 0.01\n");
1701 }
1702 
1703 /*
1704  * usage (from the wiki)
1705  */
1706 
1707 void
usage(void)1708 usage(void)
1709 {
1710 	fprintf(stderr, "%s",
1711 "perftcpdns [-huvX0] [-4|-6] [-r<rate>] [-t<report>] [-p<test-period>]\n"
1712 "    [-n<num-request>]* [-d<lost-time>]* [-D<max-loss>]* [-T<template-file>]\n"
1713 "    [-l<local-addr>] [-L<local-port>]* [-a<aggressiveness>] [-s<seed>]\n"
1714 "    [-M<memory>] [-x<diagnostic-selector>] [-P<port>] server\n"
1715 "\f\n"
1716 "The server argument is the name/address of the DNS server to contact.\n"
1717 "\n"
1718 "Options:\n"
1719 "-0: Add EDNS0 option with DO flag.\n"
1720 "-4: TCP/IPv4 operation (default). This is incompatible with the -6 option.\n"
1721 "-6: TCP/IPv6 operation. This is incompatible with the -4 option.\n"
1722 "-a<aggressiveness>: When the target sending rate is not yet reached,\n"
1723 "    control how many connections are initiated before the next pause.\n"
1724 "-d<lost-time>: Specify the time after which a connection or a query is\n"
1725 "    treated as having been lost. The value is given in seconds and\n"
1726 "    may contain a fractional component. The default is 1 second.\n"
1727 "-h: Print this help.\n"
1728 "-l<local-addr>: Specify the local hostname/address to use when\n"
1729 "    communicating with the server.\n"
1730 "-L<local-port>: Specify the (minimal and maximal) local port number\n"
1731 "-M<memory>: Size of the tables (default 60000)\n"
1732 "-P<port>: Specify an alternate (i.e., not 53) port\n"
1733 "-r<rate>: Initiate <rate> TCP DNS connections per second.  A periodic\n"
1734 "    report is generated showing the number of exchanges which were not\n"
1735 "    completed, as well as the average response latency.  The program\n"
1736 "    continues until interrupted, at which point a final report is\n"
1737 "    generated.\n"
1738 "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
1739 "-t<report>: Delay in seconds between two periodic reports.\n"
1740 "-T<template-file>: The name of a file containing the template to use\n"
1741 "    as a stream of hexadecimal digits.\n"
1742 "-u: Use UDP in place of TCP.\n"
1743 "-v: Report the version number of this program.\n"
1744 "-X: change default template to get NXDOMAIN responses.\n"
1745 "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
1746 "    <diagnostic-selector> is a string of single-keywords specifying\n"
1747 "    the operations for which verbose output is desired.  The selector\n"
1748 "    keyletters are:\n"
1749 "   * 'a': print the decoded command line arguments\n"
1750 "   * 'e': print the exit reason\n"
1751 "   * 'i': print rate processing details\n"
1752 "   * 'T': when finished, print templates\n"
1753 "\n"
1754 "Stopping conditions:\n"
1755 "-D<max-loss>: Abort the test if more than <max-loss> connections or\n"
1756 "   queries have been lost.  If <max-loss> includes the suffix '%', it\n"
1757 "   specifies a maximum percentage of losses before stopping.\n"
1758 "   In this case, testing of the threshold begins after 10\n"
1759 "   connections/responses have been expected to be accepted/received.\n"
1760 "-n<num-request>: Initiate <num-request> transactions.  No report is\n"
1761 "    generated until all transactions have been initiated/waited-for,\n"
1762 "    after which a report is generated and the program terminates.\n"
1763 "-p<test-period>: Send requests for the given test period, which is\n"
1764 "    specified in the same manner as -d.  This can be used as an\n"
1765 "    alternative to -n, or both options can be given, in which case the\n"
1766 "    testing is completed when either limit is reached.\n"
1767 "\n"
1768 "Errors:\n"
1769 "- locallimit: reached to local system limits when sending a message.\n"
1770 "- badconn: connection failed (from getsockopt(SO_ERROR))\n"
1771 "- collconn: connect() timed out\n"
1772 "- badsent: send() failed\n"
1773 "- callsent: timed out waiting from a response\n"
1774 "- recverr: recv() system call failed\n"
1775 "- tooshort: received a too short message\n"
1776 "- badid: the id mismatches between the query and the response\n"
1777 "- notresp: doesn't receive a response\n"
1778 "Rate stats:\n"
1779 "- loops: number of thread loop iterations\n"
1780 "- shortwait: no direct activity in a thread iteration\n"
1781 "- compconn: computed number of connect() calls\n"
1782 "- lateconn: connect() already dued when computing delay to the next one\n"
1783 "\n"
1784 "Exit status:\n"
1785 "The exit status is:\n"
1786 "0 on complete success.\n"
1787 "1 for a general error.\n"
1788 "2 if an error is found in the command line arguments.\n"
1789 "3 if there are no general failures in operation, but one or more\n"
1790 "  exchanges are not successfully completed.\n");
1791 }
1792 
1793 /*
1794  * main function / entry point
1795  */
1796 
1797 int
main(const int argc,char * const argv[])1798 main(const int argc, char * const argv[])
1799 {
1800 	int opt, flags = 0, ret, i;
1801 	long long r;
1802 	char *pc;
1803 	double d;
1804 	extern char *optarg;
1805 	extern int optind;
1806 
1807 #define OPTIONS	"hv46u0XM:r:t:R:b:n:p:d:D:l:L:a:s:T:O:x:P:"
1808 
1809 	/* decode options */
1810 	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
1811 	switch (opt) {
1812 	case 'h':
1813 		usage();
1814 		exit(0);
1815 
1816 	case 'u':
1817 		udp = 1;
1818 		break;
1819 
1820 	case 'v':
1821 		version();
1822 		exit(0);
1823 
1824 	case '0':
1825 		edns0 = 1;
1826 		break;
1827 
1828 	case '4':
1829 		if (ipversion == 6) {
1830 			fprintf(stderr, "IP version already set to 6\n");
1831 			usage();
1832 			exit(2);
1833 		}
1834 		ipversion = 4;
1835 		break;
1836 
1837 	case '6':
1838 		if (ipversion == 4) {
1839 			fprintf(stderr, "IP version already set to 4\n");
1840 			usage();
1841 			exit(2);
1842 		}
1843 		ipversion = 6;
1844 		break;
1845 
1846 	case 'X':
1847 		ixann = 1;
1848 		break;
1849 
1850 	case 'M':
1851 		xlast = atoi(optarg);
1852 		if (xlast <= 1000) {
1853 			fprintf(stderr, "memory must be greater than 1000\n");
1854 			usage();
1855 			exit(2);
1856 		}
1857 		break;
1858 
1859 	case 'r':
1860 		rate = atoi(optarg);
1861 		if (rate <= 0) {
1862 			fprintf(stderr, "rate must be a positive integer\n");
1863 			usage();
1864 			exit(2);
1865 		}
1866 		break;
1867 
1868 	case 't':
1869 		report = atoi(optarg);
1870 		if (report <= 0) {
1871 			fprintf(stderr, "report must be a positive integer\n");
1872 			usage();
1873 			exit(2);
1874 		}
1875 		break;
1876 
1877 	case 'R':
1878 		r = atoll(optarg);
1879 		if (r < 0) {
1880 			fprintf(stderr,
1881 				"range must not be a negative integer\n");
1882 			usage();
1883 			exit(2);
1884 		}
1885 		range = (uint32_t) r;
1886 		if ((range != 0) && (range != UINT32_MAX)) {
1887 			uint32_t s = range + 1;
1888 			uint64_t b = UINT32_MAX + 1, m;
1889 
1890 			m = (b / s) * s;
1891 			if (m == b)
1892 				maxrandom = 0;
1893 			else
1894 				maxrandom = (uint32_t) m;
1895 		}
1896 		break;
1897 
1898 	case 'b':
1899 		if (basecnt > 1) {
1900 			fprintf(stderr, "too many bases\n");
1901 			usage();
1902 			exit(2);
1903 		}
1904 		base[basecnt] = optarg;
1905 		/* decodebase(); */
1906 		basecnt++;
1907 		break;
1908 
1909 	case 'n':
1910 		noreport = 1;
1911 		gotnumreq++;
1912 		if (gotnumreq > 1) {
1913 			fprintf(stderr, "too many num-request's\n");
1914 			usage();
1915 			exit(2);
1916 		}
1917 		numreq[gotnumreq] = atoi(optarg);
1918 		if ((numreq[gotnumreq] < 0) ||
1919 		    ((numreq[gotnumreq] == 0) && (gotnumreq == 1))) {
1920 			fprintf(stderr,
1921 				"num-request must be a positive integer\n");
1922 			usage();
1923 			exit(2);
1924 		}
1925 		break;
1926 
1927 	case 'p':
1928 		noreport = 1;
1929 		period = atoi(optarg);
1930 		if (period <= 0) {
1931 			fprintf(stderr,
1932 				"test-period must be a positive integer\n");
1933 			usage();
1934 			exit(2);
1935 		}
1936 		break;
1937 
1938 	case 'd':
1939 		gotlosttime++;
1940 		if (gotlosttime > 1) {
1941 			fprintf(stderr, "too many lost-time's\n");
1942 			usage();
1943 			exit(2);
1944 		}
1945 		d = atof(optarg);
1946 		if ((d < 0.) || ((d == 0.) && (gotlosttime == 1))) {
1947 			fprintf(stderr,
1948 				"lost-time must be a positive number\n");
1949 			usage();
1950 			exit(2);
1951 		}
1952 		if (d > 0.)
1953 			losttime[gotlosttime] = d;
1954 		break;
1955 
1956 	case 'D':
1957 		noreport = 1;
1958 		gotmaxloss++;
1959 		if (gotmaxloss > 1) {
1960 			fprintf(stderr, "too many max-loss's\n");
1961 			usage();
1962 			exit(2);
1963 		}
1964 		pc = strchr(optarg, '%');
1965 		if (pc != NULL) {
1966 			*pc = '\0';
1967 			maxploss[gotmaxloss] = atof(optarg);
1968 			if ((maxploss[gotmaxloss] < 0) ||
1969 			    (maxploss[gotmaxloss] >= 100)) {
1970 				fprintf(stderr,
1971 					"invalid max-loss percentage\n");
1972 				usage();
1973 				exit(2);
1974 			}
1975 		} else {
1976 			maxloss[gotmaxloss] = atoi(optarg);
1977 			if ((maxloss[gotmaxloss] < 0) ||
1978 			    ((maxloss[gotmaxloss] == 0) &&
1979 			     (gotmaxloss == 1))) {
1980 				fprintf(stderr,
1981 					"max-loss must be a "
1982 					"positive integer\n");
1983 				usage();
1984 				exit(2);
1985 			}
1986 		}
1987 		break;
1988 
1989 	case 'l':
1990 		localname = optarg;
1991 		break;
1992 
1993 	case 'L':
1994 		i = atoi(optarg);
1995 		if ((i <= 0) || (i >65535)) {
1996 			fprintf(stderr,
1997 				"local-port must be a small positive integer\n");
1998 			usage();
1999 			exit(2);
2000 		}
2001 		if (maxport != 0) {
2002 			fprintf(stderr, "too many local-port's\n");
2003 			usage();
2004 			exit(2);
2005 		}
2006 		if (curport == 0)
2007 			minport = curport = i;
2008 		else
2009 			maxport = i;
2010 		break;
2011 
2012 	case 'a':
2013 		aggressiveness = atoi(optarg);
2014 		if (aggressiveness <= 0) {
2015 			fprintf(stderr,
2016 				"aggressiveness must be a positive integer\n");
2017 			usage();
2018 			exit(2);
2019 		}
2020 		break;
2021 
2022 	case 's':
2023 		seeded = 1;
2024 		seed = (unsigned int) atol(optarg);
2025 		break;
2026 
2027 	case 'T':
2028 		if (templatefile != NULL) {
2029 			fprintf(stderr, "template-file is already set\n");
2030 			usage();
2031 			exit(2);
2032 		}
2033 		templatefile = optarg;
2034 		break;
2035 
2036 	case 'O':
2037 		rndoffset = atoi(optarg);
2038 		if (rndoffset < 14) {
2039 			fprintf(stderr,
2040 				"random-offset must be greater than 14\n");
2041 			usage();
2042 			exit(2);
2043 		}
2044 		break;
2045 
2046 	case 'x':
2047 		diags = optarg;
2048 		break;
2049 
2050 	case 'P':
2051 		i = atoi(optarg);
2052 		if ((i <= 0) || (i > 65535)) {
2053 			fprintf(stderr,
2054 				"port must be a positive short integer\n");
2055 			usage();
2056 			exit(2);
2057 		}
2058 		port = (in_port_t) i;
2059 		break;
2060 
2061 	default:
2062 		usage();
2063 		exit(2);
2064 	}
2065 
2066 	/* adjust some global variables */
2067 	if (ipversion == 0)
2068 		ipversion = 4;
2069 	if (rate == 0)
2070 		rate = 100;
2071 	if (xlast == 0)
2072 		xlast = 60000;
2073 	if (noreport == 0)
2074 		report = 1;
2075 	if ((curport != 0) && (maxport == 0))
2076 		maxport = 65535;
2077 
2078 	/* when required, print the internal view of the command line */
2079 	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
2080 		if (udp)
2081 			printf("UDP ");
2082 		printf("IPv%d", ipversion);
2083 		printf(" rate=%d", rate);
2084 		if (edns0 != 0)
2085 			printf(" EDNS0");
2086 		if (report != 0)
2087 			printf(" report=%d", report);
2088 		if (range != 0) {
2089 			if (strchr(diags, 'r') != NULL)
2090 				printf(" range=0..%d [0x%x]",
2091 				       range,
2092 				       (unsigned int) maxrandom);
2093 			else
2094 				printf(" range=0..%d", range);
2095 		}
2096 		if (basecnt != 0)
2097 			for (i = 0; i < basecnt; i++)
2098 				printf(" base[%d]='%s'", i, base[i]);
2099 		if (gotnumreq >= 0) {
2100 			if ((numreq[0] == 0) && (numreq[1] != 0))
2101 				printf(" num-request=*,%d", numreq[1]);
2102 			if ((numreq[0] != 0) && (numreq[1] == 0))
2103 				printf(" num-request=%d,*", numreq[0]);
2104 			if ((numreq[0] != 0) && (numreq[1] != 0))
2105 				printf(" num-request=%d,%d",
2106 				       numreq[0], numreq[1]);
2107 		}
2108 		if (period != 0)
2109 			printf(" test-period=%d", period);
2110 		printf(" lost-time=%g,%g", losttime[0], losttime[1]);
2111 		if (gotmaxloss == 0) {
2112 			if (maxloss[0] != 0)
2113 				printf(" max-loss=%d,*", maxloss[0]);
2114 			if (maxploss[0] != 0.)
2115 				printf(" max-loss=%2.2f%%,*", maxploss[0]);
2116 		} else if (gotmaxloss == 1) {
2117 			if (maxloss[0] != 0)
2118 				printf(" max-loss=%d,", maxloss[0]);
2119 			else if (maxploss[0] != 0.)
2120 				printf(" max-loss=%2.2f%%,", maxploss[0]);
2121 			else
2122 				printf(" max-loss=*,");
2123 			if (maxloss[1] != 0)
2124 				printf("%d", maxloss[1]);
2125 			else if (maxploss[1] != 0.)
2126 				printf("%2.2f%%", maxploss[1]);
2127 			else
2128 				printf("*");
2129 		}
2130 		printf(" aggressiveness=%d", aggressiveness);
2131 		if (seeded)
2132 			printf(" seed=%u", seed);
2133 		if (templatefile != NULL)
2134 			printf(" template-file='%s'", templatefile);
2135 		else if (ixann != 0)
2136 			printf(" Xflag");
2137 		if (rndoffset >= 0)
2138 			printf(" rnd-offset=%d", rndoffset);
2139 		printf(" diagnotic-selectors='%s'", diags);
2140 		printf("\n");
2141 	}
2142 
2143 	/* check local address options */
2144 	if ((localname == NULL) && (curport != 0)) {
2145 		fprintf(stderr,
2146 			"-l<local-addr> must be set to use -L<local-port>\n");
2147 		usage();
2148 		exit(2);
2149 	}
2150 
2151 	/* check template file options */
2152 	if ((templatefile == NULL) && (rndoffset >= 0)) {
2153 		fprintf(stderr,
2154 			"-T<template-file> must be set to "
2155 			"use -O<random-offset>\n");
2156 		usage();
2157 		exit(2);
2158 	}
2159 
2160 	/* check various template file(s) and other condition(s) options */
2161 	if ((templatefile != NULL) && (range > 0) && (rndoffset < 0)) {
2162 		fprintf(stderr,
2163 			"-O<random-offset> must be set when "
2164 			"-T<template-file> and -R<range> are used\n");
2165 		usage();
2166 		exit(2);
2167 	}
2168 
2169 	/* get the server argument */
2170 	if (optind < argc - 1) {
2171 		fprintf(stderr, "extra arguments?\n");
2172 		usage();
2173 		exit(2);
2174 	}
2175 	if (optind == argc - 1)
2176 		servername = argv[optind];
2177 
2178 	/* handle the local '-l' address/interface */
2179 	if (localname != NULL) {
2180 		/* given */
2181 		getlocaladdr();
2182 		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
2183 			printf("local-addr='%s'", localname);
2184 			if (curport != 0)
2185 				printf(" local-port='%d..%d'",
2186 				       minport, maxport);
2187 			printf("\n");
2188 		}
2189 	}
2190 
2191 	/* get the server socket address */
2192 	if (servername == NULL) {
2193 		fprintf(stderr, "server is required\n");
2194 		usage();
2195 		exit(2);
2196 	}
2197 	getserveraddr(flags);
2198 
2199 	/* finish local/server socket address stuff and print it */
2200 	if ((diags != NULL) && (strchr(diags, 'a') != NULL))
2201 		printf("server='%s'\n", servername);
2202 	if ((localname != NULL) &&
2203 	    (diags != NULL) && (strchr(diags, 'a') != NULL)) {
2204 		char addr[NI_MAXHOST];
2205 
2206 		ret = getnameinfo((struct sockaddr *) &localaddr,
2207 				  sizeof(localaddr),
2208 				  addr,
2209 				  NI_MAXHOST,
2210 				  NULL,
2211 				  0,
2212 				  NI_NUMERICHOST);
2213 		if (ret != 0) {
2214 			fprintf(stderr,
2215 				"can't get the local address: %s\n",
2216 				gai_strerror(ret));
2217 			exit(1);
2218 		}
2219 		printf("local address='%s'\n", addr);
2220 	}
2221 
2222 	/* initialize exchange structures */
2223 	inits();
2224 
2225 	/* get the socket descriptor and template(s) */
2226 	if (templatefile == NULL)
2227 		build_template_query();
2228 	else
2229 		get_template_query();
2230 
2231 	/* boot is done! */
2232 	if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
2233 		perror("clock_gettime(boot)");
2234 		exit(1);
2235 	}
2236 
2237 	/* compute the next intermediate reporting date */
2238 	if (report != 0) {
2239 		dreport.tv_sec = boot.tv_sec + report;
2240 		dreport.tv_nsec = boot.tv_nsec;
2241 	}
2242 
2243 	/* seed the random generator */
2244 	if (seeded == 0)
2245 		seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
2246 	srandom(seed);
2247 
2248 	/* required only before the interrupted flag check */
2249 	(void) signal(SIGINT, interrupt);
2250 	(void) signal(SIGTERM, terminate);
2251 
2252 	/* threads */
2253 	master = pthread_self();
2254 	ret = pthread_create(&connector, NULL, connecting, NULL);
2255 	if (ret != 0) {
2256 		fprintf(stderr, "pthread_create: %s\n", strerror(ret));
2257 		exit(1);
2258 	}
2259 	ret = pthread_create(&sender, NULL, sending, NULL);
2260 	if (ret != 0) {
2261 		fprintf(stderr, "pthread_create: %s\n", strerror(ret));
2262 		exit(1);
2263 	}
2264 	ret = pthread_create(&receiver, NULL, receiving, NULL);
2265 	if (ret != 0) {
2266 		fprintf(stderr, "pthread_create: %s\n", strerror(ret));
2267 		exit(1);
2268 	}
2269 
2270 	/* main loop */
2271 	for (;;) {
2272 		struct timespec now, ts;
2273 
2274 		/* immediate loop exit conditions */
2275 		if (interrupted) {
2276 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2277 				printf("interrupted\n");
2278 			break;
2279 		}
2280 		if (fatal) {
2281 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2282 				printf("got a fatal error\n");
2283 			break;
2284 		}
2285 
2286 		loops[0]++;
2287 
2288 		/* get the date and use it */
2289 		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
2290 			perror("clock_gettime(now)");
2291 			fatal = 1;
2292 			continue;
2293 		}
2294 		if ((period != 0) &&
2295 		    ((boot.tv_sec + period < now.tv_sec) ||
2296 		     ((boot.tv_sec + period == now.tv_sec) &&
2297 		      (boot.tv_nsec < now.tv_nsec)))) {
2298 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2299 				printf("reached test-period\n");
2300 			break;
2301 		}
2302 		if ((report != 0) &&
2303 		    ((dreport.tv_sec < now.tv_sec) ||
2304 		     ((dreport.tv_sec == now.tv_sec) &&
2305 		      (dreport.tv_nsec < now.tv_nsec))))
2306 			reporting();
2307 
2308 		/* check receive loop exit conditions */
2309 		if ((numreq[0] != 0) && ((int) xccount >= numreq[0])) {
2310 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2311 				printf("reached num-connection\n");
2312 			break;
2313 		}
2314 		if ((numreq[1] != 0) && ((int) xscount >= numreq[1])) {
2315 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2316 				printf("reached num-query\n");
2317 			break;
2318 		}
2319 		if ((maxloss[0] != 0) &&
2320 		    ((int) (xccount - xscount) > maxloss[0])) {
2321 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2322 				printf("reached max-loss "
2323 				       "(connection/absolute)\n");
2324 			break;
2325 		}
2326 		if ((maxloss[1] != 0) &&
2327 		    ((int) (xscount - xrcount) > maxloss[1])) {
2328 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2329 				printf("reached max-loss "
2330 				       "(query/absolute)\n");
2331 			break;
2332 		}
2333 		if ((maxploss[0] != 0.) &&
2334 		    (xccount > 10) &&
2335 		    (((100. * (xccount - xscount)) / xccount) > maxploss[1])) {
2336 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2337 				printf("reached max-loss "
2338 				       "(connection/percent)\n");
2339 			break;
2340 		}
2341 		if ((maxploss[1] != 0.) &&
2342 		    (xscount > 10) &&
2343 		    (((100. * (xscount - xrcount)) / xscount) > maxploss[1])) {
2344 			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2345 				printf("reached max-loss "
2346 				       "(query/percent)\n");
2347 			break;
2348 		}
2349 
2350 		/* waiting 1ms */
2351 		memset(&ts, 0, sizeof(ts));
2352 		ts.tv_nsec = 1000000;
2353 		(void) clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
2354 	}
2355 
2356 	/* after main loop: finished */
2357 	if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
2358 		perror("clock_gettime(finished)");
2359 
2360 	/* threads */
2361 	(void) pthread_cancel(connector);
2362 	(void) pthread_cancel(sender);
2363 	(void) pthread_cancel(receiver);
2364 
2365 	/* main statictics */
2366 	printf("connect: %llu, sent: %llu, received: %llu\n",
2367 	       (unsigned long long) xccount,
2368 	       (unsigned long long) xscount,
2369 	       (unsigned long long) xrcount);
2370 	printf("embryonics: %lld (%.1f%%)\n",
2371 	       (long long) (xccount - xscount),
2372 	       (100. * (xccount - xscount)) / xccount);
2373 	printf("drops: %lld (%.1f%%)\n",
2374 	       (long long) (xscount - xrcount),
2375 	       (100. * (xscount - xrcount)) / xscount);
2376 	printf("total losses: %lld (%.1f%%)\n",
2377 	       (long long) (xccount - xrcount),
2378 	       (100. * (xccount - xrcount)) / xccount);
2379 	printf("local limits: %llu, bad connects: %llu, "
2380 	       "connect timeouts: %llu\n",
2381 	       (unsigned long long) locallimit,
2382 	       (unsigned long long) badconn,
2383 	       (unsigned long long) collconn);
2384 	printf("bad sends: %llu, bad recvs: %llu, recv timeouts: %llu\n",
2385 	       (unsigned long long) badsent,
2386 	       (unsigned long long) recverr,
2387 	       (unsigned long long) collsent);
2388 	printf("too shorts: %llu, bad IDs: %llu, not responses: %llu\n",
2389 	       (unsigned long long) tooshort,
2390 	       (unsigned long long) badid,
2391 	       (unsigned long long) notresp);
2392 	printf("rcode counters:\n noerror: %llu, formerr: %llu, "
2393 	       "servfail: %llu\n "
2394 	       "nxdomain: %llu, noimp: %llu, refused: %llu, others: %llu\n",
2395 	       (unsigned long long) rcodes[NS_RCODE_NOERROR],
2396 	       (unsigned long long) rcodes[NS_RCODE_FORMERR],
2397 	       (unsigned long long) rcodes[NS_RCODE_SERVFAIL],
2398 	       (unsigned long long) rcodes[NS_RCODE_NXDOMAIN],
2399 	       (unsigned long long) rcodes[NS_RCODE_NOIMP],
2400 	       (unsigned long long) rcodes[NS_RCODE_REFUSED],
2401 	       (unsigned long long) rcodes[NS_RCODE_LAST]);
2402 
2403 	/* print the rates */
2404 	if (finished.tv_sec != 0) {
2405 		double dall, erate[3];
2406 
2407 		dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
2408 		dall += finished.tv_sec - boot.tv_sec;
2409 		erate[0] = xccount / dall;
2410 		erate[1] = xscount / dall;
2411 		erate[2] = xrcount / dall;
2412 		printf("rates: %.0f,%.0f,%.0f (target %d)\n",
2413 		       erate[0], erate[1], erate[2], rate);
2414 	}
2415 
2416 	/* rate processing instrumentation */
2417 	if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
2418 		printf("loops: %llu,%llu,%llu,%llu\n",
2419 		       (unsigned long long) loops[0],
2420 		       (unsigned long long) loops[1],
2421 		       (unsigned long long) loops[2],
2422 		       (unsigned long long) loops[3]);
2423 		printf("shortwait: %llu,%llu,%llu\n",
2424 		       (unsigned long long) shortwait[0],
2425 		       (unsigned long long) shortwait[1],
2426 		       (unsigned long long) shortwait[2]);
2427 		printf("compconn: %llu, lateconn: %llu\n",
2428 		       (unsigned long long) compconn,
2429 		       (unsigned long long) lateconn);
2430 		printf("badconn: %llu, collconn: %llu, "
2431 		       "recverr: %llu, collsent: %llu\n",
2432 		       (unsigned long long) badconn,
2433 		       (unsigned long long) collconn,
2434 		       (unsigned long long) recverr,
2435 		       (unsigned long long) collsent);
2436 		printf("memory: used(%d) / allocated(%d)\n",
2437 		       xused, xlast);
2438 	}
2439 
2440 	/* round-time trip statistics */
2441 	if (xrcount != 0) {
2442 		double avg, stddev;
2443 
2444 		avg = dsum / xrcount;
2445 		stddev = sqrt(dsumsq / xrcount - avg * avg);
2446 		printf("RTT: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
2447 		       dmin * 1e3, avg * 1e3, dmax * 1e3, stddev * 1e3);
2448 	}
2449 	printf("\n");
2450 
2451 	/* template(s) */
2452 	if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
2453 		size_t n;
2454 
2455 		printf("length = 0x%zx\n", length_query);
2456 		if (random_query > 0)
2457 			printf("random offset = %zu\n", random_query);
2458 		printf("content:\n");
2459 		for (n = 0; n < length_query; n++) {
2460 			printf("%s%02hhx",
2461 			       (n & 15) == 0 ? "" : " ",
2462 			       template_query[n]);
2463 			if ((n & 15) == 15)
2464 				printf("\n");
2465 		}
2466 		if ((n & 15) != 15)
2467 			printf("\n");
2468 		printf("\n");
2469 	}
2470 
2471 	/* compute the exit code (and exit) */
2472 	if (fatal)
2473 		exit(1);
2474 	else if ((xccount == xscount) && (xscount == xrcount))
2475 		exit(0);
2476 	else
2477 		exit(3);
2478 }
2479