etc/services - sync with NetBSD-8
[minix.git] / external / bsd / bind / dist / contrib / perftcpdns / perftcpdns.c
blob98a8b49be9b83193d679649c94e7926a47e2ce11
1 /* $NetBSD: perftcpdns.c,v 1.3 2014/12/10 04:37:56 christos Exp $ */
3 /*
4 * Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
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.
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.
20 * TCP DNS perf tool
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.
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.
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.
68 #ifdef __linux__
69 #define _GNU_SOURCE
70 #endif
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>
79 #include <netinet/in.h>
80 #include <arpa/inet.h>
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>
96 /* DNS defines */
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
119 #define NS_CLASS_IN 1
120 #define NS_CLASS_ANY 255
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
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
138 #define NS_XFLAG_DO 0x8000U
140 #define NS_OPCODE_MASK 0x7000U
141 #define NS_OPCODE_QUERY 0
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
152 /* chaining macros */
154 #define ISC_INIT(head, headl) do { \
155 (head) = -1; \
156 (headl) = &(head); \
157 } while (/*CONSTCOND*/0)
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)
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)
175 * Data structures
179 * exchange:
180 * - per exchange values:
181 * * order (for debugging)
182 * * id
183 * * random (for debugging)
184 * * time-stamps
186 * sent/rcvd chain, "next to be received" on entry cache.
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 */
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 */
214 * statictics counters and accumulators
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 */
229 * command line parameters
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 */
262 * global variables
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 */
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 */
277 uint8_t obuf[4098], ibuf[4098]; /* I/O buffers */
278 char tbuf[4098]; /* template buffer */
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 */
287 * template
290 size_t length_query;
291 uint8_t template_query[4096];
292 size_t random_query;
295 * threads
298 pthread_t master, connector, sender, receiver;
301 * initialize data structures handling exchanges
304 void
305 inits(void)
307 int idx;
309 ISC_INIT(xconn, xconnl);
310 ISC_INIT(xready, xreadyl);
311 ISC_INIT(xsent, xsentl);
312 ISC_INIT(xfree, xfreel);
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);
321 epoll_ifd = epoll_create(EVENTS_CNT);
322 if (epoll_ifd < 0) {
323 perror("epoll_create(input)");
324 exit(1);
326 epoll_ofd = epoll_create(EVENTS_CNT);
327 if (epoll_ofd < 0) {
328 perror("epoll_create(output)");
329 exit(1);
332 xlist = (struct exchange *) malloc(xlast * sizeof(struct exchange));
333 if (xlist == NULL) {
334 perror("malloc(exchanges)");
335 exit(1);
337 memset(xlist, 0, xlast * sizeof(struct exchange));
339 for (idx = 0; idx < xlast; idx++)
340 xlist[idx].sock = xlist[idx].next = -1;
344 * build a TCP DNS QUERY
347 void
348 build_template_query(void)
350 uint8_t *p = template_query;
351 uint16_t v;
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;
426 /* length */
427 length_query = p - template_query;
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>)
436 void
437 get_template_query(void)
439 uint8_t *p = template_query;
440 int fd, cc, i, j;
442 fd = open(templatefile, O_RDONLY);
443 if (fd < 0) {
444 fprintf(stderr, "open(%s): %s\n",
445 templatefile, strerror(errno));
446 exit(2);
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);
455 if (cc < NS_OFF_QUESTION + 6) {
456 fprintf(stderr,"file '%s' too small\n", templatefile);
457 exit(2);
459 if (cc > 4096) {
460 fprintf(stderr,"file '%s' too large\n", templatefile);
461 exit(2);
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);
473 tbuf[j] = tbuf[i];
474 j++;
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);
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);
496 #if 0
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
504 uint32_t
505 randomize(size_t offset, uint32_t r)
507 uint32_t v;
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;
537 #endif
540 * flush/timeout connect
543 void
544 flushconnect(void)
546 struct exchange *x;
547 struct timespec now;
548 int idx = xconn;
549 int cnt = 10;
550 double waited;
552 if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
553 perror("clock_gettime(flushconnect)");
554 fatal = 1;
555 (void) pthread_kill(master, SIGTERM);
556 return;
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;
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;
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;
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;
606 * poll connected
609 void
610 pollconnect(int topoll)
612 struct exchange *x;
613 int evn, idx, err;
614 socklen_t len = sizeof(int);
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;
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;
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;
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;
659 continue;
661 x->state = X_READY;
662 ISC_INSERT(xready, xreadyl, x);
667 * send the TCP DNS QUERY
671 sendquery(struct exchange *x)
673 ssize_t ret;
674 size_t off;
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;
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;
702 ret = send(x->sock, obuf, length_query + off, 0);
703 if ((size_t) ret == length_query + off)
704 return 0;
705 return -errno;
709 * poll ready and send
712 void
713 pollsend(void)
715 struct exchange *x;
716 int idx = xready;
717 struct epoll_event ev;
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;
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;
748 continue;
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;
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;
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;
775 * receive a TCP DNS RESPONSE
778 void
779 receiveresp(struct exchange *x)
781 struct timespec now;
782 ssize_t cc;
783 size_t off;
784 uint16_t v;
785 double delta;
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;
796 perror("recv");
797 fatal = 1;
798 (void) pthread_kill(master, SIGTERM);
799 return;
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;
810 /* must match the ID */
811 if (memcmp(ibuf + off + NS_OFF_ID, &x->id, 2) != 0) {
812 badid++;
813 return;
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;
822 if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
823 perror("clock_gettime(receive)");
824 fatal = 1;
825 (void) pthread_kill(master, SIGTERM);
826 return;
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;
846 * flush/timeout receive
849 void
850 flushrecv(void)
852 struct exchange *x;
853 struct timespec now;
854 int idx = xsent;
855 int cnt = 5;
856 double waited;
858 if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
859 perror("clock_gettime(receive)");
860 fatal = 1;
861 (void) pthread_kill(master, SIGTERM);
862 return;
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;
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;
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;
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;
912 * poll receive
915 void
916 pollrecv(int topoll)
918 struct exchange *x;
919 int evn, idx;
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;
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;
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;
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;
963 * get the TCP DNS socket descriptor (IPv4)
967 getsock4(void)
969 int sock;
970 int flags;
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;
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;
986 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
987 (void) close(sock);
988 return -errno;
991 /* bind if wanted */
992 if (localname != NULL) {
993 if (curport) {
994 struct sockaddr_in *l4;
996 l4 = (struct sockaddr_in *) &localaddr;
997 l4->sin_port = htons((uint16_t) curport);
998 curport++;
999 if (curport > maxport)
1000 curport = minport;
1002 if (bind(sock,
1003 (struct sockaddr *) &localaddr,
1004 sizeof(struct sockaddr_in)) < 0) {
1005 (void) close(sock);
1006 return -errno;
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;
1019 return sock;
1023 * connect the TCP DNS QUERY (IPv4)
1027 connect4(void)
1029 struct exchange *x;
1030 int ret;
1031 int idx;
1032 struct epoll_event ev;
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;
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;
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;
1060 } else if (xused < xlast) {
1061 idx = xused;
1062 x = xlist + idx;
1063 xused++;
1064 } else
1065 return -ENOMEM;
1067 if ((x->state != X_FREE) || (x->sock != -1))
1068 abort();
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;
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;
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;
1095 return result;
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;
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;
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;
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;
1131 * get the TCP DNS socket descriptor (IPv6)
1135 getsock6(void)
1137 int sock;
1138 int flags;
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;
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;
1154 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
1155 (void) close(sock);
1156 return -errno;
1159 /* bind if wanted */
1160 if (localname != NULL) {
1161 if (curport) {
1162 struct sockaddr_in6 *l6;
1164 l6 = (struct sockaddr_in6 *) &localaddr;
1165 l6->sin6_port = htons((uint16_t) curport);
1166 curport++;
1167 if (curport > maxport)
1168 curport = minport;
1170 if (bind(sock,
1171 (struct sockaddr *) &localaddr,
1172 sizeof(struct sockaddr_in6)) < 0) {
1173 (void) close(sock);
1174 return -errno;
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;
1187 return sock;
1191 * connect the TCP DNS QUERY (IPv6)
1195 connect6(void)
1197 struct exchange *x;
1198 int ret;
1199 int idx;
1200 struct epoll_event ev;
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;
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;
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;
1228 } else if (xused < xlast) {
1229 idx = xused;
1230 x = xlist + idx;
1231 xused++;
1232 } else
1233 return -ENOMEM;
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;
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;
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;
1260 return result;
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;
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;
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;
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;
1296 * connector working routine
1299 void *
1300 connecting(void *dummy)
1302 struct timespec now, ts;
1303 int ret;
1304 int i;
1305 char name[16];
1307 dummy = dummy;
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");
1322 for (;;) {
1323 if (fatal)
1324 break;
1326 loops[1]++;
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;
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;
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;
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;
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;
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;
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;
1412 } else
1413 /* there was no connection to open */
1414 shortwait[0]++;
1417 return NULL;
1421 * sender working routine
1424 void *
1425 sending(void *dummy)
1427 int ret;
1428 int nfds;
1429 char name[16];
1431 dummy = dummy;
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");
1446 for (;;) {
1447 if (fatal)
1448 break;
1450 loops[2]++;
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;
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;
1475 /* packet(s) to send */
1476 pollsend();
1477 if (fatal)
1478 break;
1481 return NULL;
1485 * receiver working routine
1488 void *
1489 receiving(void *dummy)
1491 int ret;
1492 int nfds;
1493 char name[16];
1495 dummy = dummy;
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");
1510 for (;;) {
1511 if (fatal)
1512 break;
1514 loops[3]++;
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;
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;
1540 return NULL;
1544 * get the server socket address from the command line:
1545 * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
1548 void
1549 getserveraddr(const int flags)
1551 struct addrinfo hints, *res;
1552 int ret;
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;
1566 hints.ai_flags = AI_ADDRCONFIG | flags;
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);
1574 if (res->ai_next != NULL) {
1575 fprintf(stderr, "ambiguous server=%s\n", servername);
1576 exit(2);
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);
1587 * get the local socket address from the command line
1590 void
1591 getlocaladdr(void)
1593 struct addrinfo hints, *res;
1594 int ret;
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;
1608 hints.ai_flags = AI_ADDRCONFIG;
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);
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);
1625 memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
1626 freeaddrinfo(res);
1630 * intermediate reporting
1631 * (note: an in-transit packet can be reported as lost)
1634 void
1635 reporting(void)
1637 dreport.tv_sec += report;
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;
1650 avg = dsum / xrcount;
1651 printf(" average: %.3f ms", avg * 1e3);
1654 printf("\n");
1658 * SIGCHLD handler
1661 void
1662 reapchild(int sig)
1664 int status;
1666 sig = sig;
1667 while (wait3(&status, WNOHANG, NULL) > 0)
1668 /* continue */;
1672 * SIGINT handler
1675 void
1676 interrupt(int sig)
1678 sig = sig;
1679 interrupted = 1;
1683 * SIGTERM handler
1686 void
1687 terminate(int sig)
1689 sig = sig;
1690 fatal = 1;
1694 * '-v' handler
1697 void
1698 version(void)
1700 fprintf(stderr, "version 0.01\n");
1704 * usage (from the wiki)
1707 void
1708 usage(void)
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");
1794 * main function / entry point
1798 main(const int argc, char * const argv[])
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;
1807 #define OPTIONS "hv46u0XM:r:t:R:b:n:p:d:D:l:L:a:s:T:O:x:P:"
1809 /* decode options */
1810 while ((opt = getopt(argc, argv, OPTIONS)) != -1)
1811 switch (opt) {
1812 case 'h':
1813 usage();
1814 exit(0);
1816 case 'u':
1817 udp = 1;
1818 break;
1820 case 'v':
1821 version();
1822 exit(0);
1824 case '0':
1825 edns0 = 1;
1826 break;
1828 case '4':
1829 if (ipversion == 6) {
1830 fprintf(stderr, "IP version already set to 6\n");
1831 usage();
1832 exit(2);
1834 ipversion = 4;
1835 break;
1837 case '6':
1838 if (ipversion == 4) {
1839 fprintf(stderr, "IP version already set to 4\n");
1840 usage();
1841 exit(2);
1843 ipversion = 6;
1844 break;
1846 case 'X':
1847 ixann = 1;
1848 break;
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);
1857 break;
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);
1866 break;
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);
1875 break;
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);
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;
1890 m = (b / s) * s;
1891 if (m == b)
1892 maxrandom = 0;
1893 else
1894 maxrandom = (uint32_t) m;
1896 break;
1898 case 'b':
1899 if (basecnt > 1) {
1900 fprintf(stderr, "too many bases\n");
1901 usage();
1902 exit(2);
1904 base[basecnt] = optarg;
1905 /* decodebase(); */
1906 basecnt++;
1907 break;
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);
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);
1925 break;
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);
1936 break;
1938 case 'd':
1939 gotlosttime++;
1940 if (gotlosttime > 1) {
1941 fprintf(stderr, "too many lost-time's\n");
1942 usage();
1943 exit(2);
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);
1952 if (d > 0.)
1953 losttime[gotlosttime] = d;
1954 break;
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);
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);
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);
1987 break;
1989 case 'l':
1990 localname = optarg;
1991 break;
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);
2001 if (maxport != 0) {
2002 fprintf(stderr, "too many local-port's\n");
2003 usage();
2004 exit(2);
2006 if (curport == 0)
2007 minport = curport = i;
2008 else
2009 maxport = i;
2010 break;
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);
2020 break;
2022 case 's':
2023 seeded = 1;
2024 seed = (unsigned int) atol(optarg);
2025 break;
2027 case 'T':
2028 if (templatefile != NULL) {
2029 fprintf(stderr, "template-file is already set\n");
2030 usage();
2031 exit(2);
2033 templatefile = optarg;
2034 break;
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);
2044 break;
2046 case 'x':
2047 diags = optarg;
2048 break;
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);
2058 port = (in_port_t) i;
2059 break;
2061 default:
2062 usage();
2063 exit(2);
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;
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);
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]);
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("*");
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");
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);
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);
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);
2169 /* get the server argument */
2170 if (optind < argc - 1) {
2171 fprintf(stderr, "extra arguments?\n");
2172 usage();
2173 exit(2);
2175 if (optind == argc - 1)
2176 servername = argv[optind];
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");
2191 /* get the server socket address */
2192 if (servername == NULL) {
2193 fprintf(stderr, "server is required\n");
2194 usage();
2195 exit(2);
2197 getserveraddr(flags);
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];
2206 ret = getnameinfo((struct sockaddr *) &localaddr,
2207 sizeof(localaddr),
2208 addr,
2209 NI_MAXHOST,
2210 NULL,
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);
2219 printf("local address='%s'\n", addr);
2222 /* initialize exchange structures */
2223 inits();
2225 /* get the socket descriptor and template(s) */
2226 if (templatefile == NULL)
2227 build_template_query();
2228 else
2229 get_template_query();
2231 /* boot is done! */
2232 if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
2233 perror("clock_gettime(boot)");
2234 exit(1);
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;
2243 /* seed the random generator */
2244 if (seeded == 0)
2245 seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
2246 srandom(seed);
2248 /* required only before the interrupted flag check */
2249 (void) signal(SIGINT, interrupt);
2250 (void) signal(SIGTERM, terminate);
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);
2259 ret = pthread_create(&sender, NULL, sending, NULL);
2260 if (ret != 0) {
2261 fprintf(stderr, "pthread_create: %s\n", strerror(ret));
2262 exit(1);
2264 ret = pthread_create(&receiver, NULL, receiving, NULL);
2265 if (ret != 0) {
2266 fprintf(stderr, "pthread_create: %s\n", strerror(ret));
2267 exit(1);
2270 /* main loop */
2271 for (;;) {
2272 struct timespec now, ts;
2274 /* immediate loop exit conditions */
2275 if (interrupted) {
2276 if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2277 printf("interrupted\n");
2278 break;
2280 if (fatal) {
2281 if ((diags != NULL) && (strchr(diags, 'e') != NULL))
2282 printf("got a fatal error\n");
2283 break;
2286 loops[0]++;
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;
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;
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();
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;
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;
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;
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;
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;
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;
2350 /* waiting 1ms */
2351 memset(&ts, 0, sizeof(ts));
2352 ts.tv_nsec = 1000000;
2353 (void) clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
2356 /* after main loop: finished */
2357 if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
2358 perror("clock_gettime(finished)");
2360 /* threads */
2361 (void) pthread_cancel(connector);
2362 (void) pthread_cancel(sender);
2363 (void) pthread_cancel(receiver);
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]);
2403 /* print the rates */
2404 if (finished.tv_sec != 0) {
2405 double dall, erate[3];
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);
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);
2440 /* round-time trip statistics */
2441 if (xrcount != 0) {
2442 double avg, stddev;
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);
2449 printf("\n");
2451 /* template(s) */
2452 if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
2453 size_t n;
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");
2466 if ((n & 15) != 15)
2467 printf("\n");
2468 printf("\n");
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);