1 /*
2 * collectd - src/dnstop.c
3 * Copyright (C) 2006 Florian octo Forster
4 * Copyright (C) 2002 The Measurement Factory, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * 3. Neither the name of The Measurement Factory nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Authors:
32 * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
33 * Florian octo Forster <octo at verplant.org>
34 */
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/stat.h>
40 #include <netinet/in.h>
42 #include <pcap.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <time.h>
49 #include <ctype.h>
50 #include <curses.h>
51 #include <assert.h>
52 #include <arpa/inet.h>
53 #include <arpa/nameser.h>
54 #ifdef __APPLE__
55 #include <arpa/nameser_compat.h>
56 #endif
58 #include <sys/socket.h>
59 #include <net/if_arp.h>
60 #include <net/if.h>
61 #include <netinet/if_ether.h>
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <netinet/udp.h>
67 #define PCAP_SNAPLEN 1460
68 #define MAX_QNAME_SZ 512
69 #ifndef ETHER_HDR_LEN
70 #define ETHER_ADDR_LEN 6
71 #define ETHER_TYPE_LEN 2
72 #define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
73 #endif
74 #ifndef ETHERTYPE_8021Q
75 #define ETHERTYPE_8021Q 0x8100
76 #endif
78 #if USE_PPP
79 #include <net/if_ppp.h>
80 #define PPP_ADDRESS_VAL 0xff /* The address byte value */
81 #define PPP_CONTROL_VAL 0x03 /* The control byte value */
82 #endif
84 #ifdef __linux__
85 #define uh_dport dest
86 #endif
88 #include "dnstop.h"
90 /*
91 * Type definitions
92 */
93 typedef struct _AgentAddr AgentAddr;
94 struct _AgentAddr {
95 struct in_addr src;
96 int count;
97 AgentAddr *next;
98 };
100 typedef struct _StringCounter StringCounter;
101 struct _StringCounter {
102 char *s;
103 int count;
104 StringCounter *next;
105 };
107 /* This struct cobbles together Source and Sld */
108 typedef struct _StringAddrCounter StringAddrCounter;
109 struct _StringAddrCounter {
110 struct in_addr src;
111 char *str;
112 int count;
113 StringAddrCounter *next;
114 };
116 typedef struct _foo foo;
117 struct _foo {
118 int cnt;
119 void *ptr;
120 };
122 typedef struct _rfc1035_header rfc1035_header;
123 struct _rfc1035_header {
124 unsigned short id;
125 unsigned int qr:1;
126 unsigned int opcode:4;
127 unsigned int aa:1;
128 unsigned int tc:1;
129 unsigned int rd:1;
130 unsigned int ra:1;
131 unsigned int rcode:4;
132 unsigned short qdcount;
133 unsigned short ancount;
134 unsigned short nscount;
135 unsigned short arcount;
136 };
138 typedef struct _AnonMap AnonMap;
139 struct _AnonMap {
140 struct in_addr real;
141 struct in_addr anon;
142 AnonMap *next;
143 };
145 typedef int Filter_t(unsigned short,
146 unsigned short,
147 const char *,
148 const struct in_addr,
149 const struct in_addr);
151 typedef int (printer)(const char *, ...);
153 /*
154 * flags/features for non-interactive mode
155 */
157 #define T_MAX 65536
158 #ifndef T_A6
159 #define T_A6 38
160 #endif
161 #ifndef T_SRV
162 #define T_SRV 33
163 #endif
164 #define C_MAX 65536
165 #define OP_MAX 16
167 /*
168 * Global variables
169 */
170 static int interactive = 1;
171 static char *device = NULL;
172 static struct in_addr ignore_addr;
173 static pcap_t *pcap = NULL;
174 static char *bpf_program_str = "udp dst port 53 and udp[10:2] & 0x8000 = 0";
175 static WINDOW *w;
176 static unsigned short port53;
177 static void (*SubReport) (void) = NULL;
178 static int (*handle_datalink) (const u_char * pkt, int len) = NULL;
179 static int Quit = 0;
180 static char *progname = NULL;
181 static int anon_flag = 0;
182 static int sld_flag = 0;
183 static int nld_flag = 0;
184 static int promisc_flag = 1;
185 static AnonMap *Anons = NULL;
187 static int query_count_intvl = 0;
188 static int query_count_total = 0;
189 int qtype_counts[T_MAX];
190 static int opcode_counts[OP_MAX];
191 static int qclass_counts[C_MAX];
192 static AgentAddr *Sources = NULL;
193 static AgentAddr *Destinations = NULL;
194 static StringCounter *Tlds = NULL;
195 static StringCounter *Slds = NULL;
196 static StringCounter *Nlds = NULL;
197 static StringAddrCounter *SSC2 = NULL;
198 static StringAddrCounter *SSC3 = NULL;
199 #ifdef __OpenBSD__
200 static struct bpf_timeval last_ts;
201 #else
202 static struct timeval last_ts;
203 #endif
205 /* Prototypes */
206 static void SldBySource_report(void);
207 static void NldBySource_report(void);
208 static void Sources_report(void);
209 static void Destinatioreport(void);
210 static void Qtypes_report(void);
211 static void Opcodes_report(void);
212 static void Tld_report(void);
213 static void Sld_report(void);
214 static void Nld_report(void);
215 static void Help_report(void);
216 static void ResetCounters(void);
218 static Filter_t UnknownTldFilter;
219 static Filter_t AforAFilter;
220 static Filter_t RFC1918PtrFilter;
221 static Filter_t *Filter = NULL;
223 static printer *print_func = (printer *) printw;
225 static struct in_addr
226 AnonMap_lookup_or_add(AnonMap ** headP, struct in_addr real)
227 {
228 AnonMap **T;
229 for (T = headP; (*T); T = &(*T)->next)
230 if ((*T)->real.s_addr == real.s_addr)
231 return (*T)->anon;
232 (*T) = calloc(1, sizeof(**T));
233 (*T)->real = real;
234 (*T)->anon.s_addr = random();
235 return (*T)->anon;
236 }
238 static char *
239 anon_inet_ntoa(struct in_addr a)
240 {
241 if (anon_flag)
242 a = AnonMap_lookup_or_add(&Anons, a);
243 return inet_ntoa(a);
244 }
246 static AgentAddr *
247 AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
248 {
249 AgentAddr **T;
250 for (T = headP; (*T); T = &(*T)->next)
251 if ((*T)->src.s_addr == a.s_addr)
252 return (*T);
253 (*T) = calloc(1, sizeof(**T));
254 (*T)->src = a;
255 return (*T);
256 }
258 static StringCounter *
259 StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
260 {
261 StringCounter **T;
262 for (T = headP; (*T); T = &(*T)->next)
263 if (0 == strcmp((*T)->s, s))
264 return (*T);
265 (*T) = calloc(1, sizeof(**T));
266 (*T)->s = strdup(s);
267 return (*T);
268 }
270 static StringAddrCounter *
271 StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
272 {
273 StringAddrCounter **T;
274 for (T = headP; (*T); T = &(*T)->next)
275 if (0 == strcmp((*T)->str, str))
276 if ((*T)->src.s_addr == a.s_addr)
277 return (*T);
278 (*T) = calloc(1, sizeof(**T));
279 (*T)->str = strdup(str);
280 (*T)->src = a;
281 return (*T);
282 }
284 static int
285 foo_cmp(const void *A, const void *B)
286 {
287 const foo *a = A;
288 const foo *b = B;
289 if (a->cnt < b->cnt)
290 return 1;
291 if (a->cnt > b->cnt)
292 return -1;
293 if (a->ptr < b->ptr)
294 return 1;
295 if (a->ptr > b->ptr)
296 return -1;
297 return 0;
298 }
300 static void
301 AgentAddr_sort(AgentAddr ** headP)
302 {
303 foo *sortme;
304 int n_agents = 0;
305 int i;
306 AgentAddr *a;
307 for (a = *headP; a; a = a->next)
308 n_agents++;
309 sortme = calloc(n_agents, sizeof(foo));
310 n_agents = 0;
311 for (a = *headP; a; a = a->next) {
312 sortme[n_agents].cnt = a->count;
313 sortme[n_agents].ptr = a;
314 n_agents++;
315 }
316 qsort(sortme, n_agents, sizeof(foo), foo_cmp);
317 for (i = 0; i < n_agents; i++) {
318 *headP = sortme[i].ptr;
319 headP = &(*headP)->next;
320 }
321 free(sortme);
322 *headP = NULL;
323 }
325 static void
326 StringCounter_sort(StringCounter ** headP)
327 {
328 foo *sortme;
329 int n_things = 0;
330 int i;
331 StringCounter *sc;
332 for (sc = *headP; sc; sc = sc->next)
333 n_things++;
334 sortme = calloc(n_things, sizeof(foo));
335 n_things = 0;
336 for (sc = *headP; sc; sc = sc->next) {
337 sortme[n_things].cnt = sc->count;
338 sortme[n_things].ptr = sc;
339 n_things++;
340 }
341 qsort(sortme, n_things, sizeof(foo), foo_cmp);
342 for (i = 0; i < n_things; i++) {
343 *headP = sortme[i].ptr;
344 headP = &(*headP)->next;
345 }
346 free(sortme);
347 *headP = NULL;
348 }
350 static void
351 StringAddrCounter_sort(StringAddrCounter ** headP)
352 {
353 foo *sortme;
354 int n_things = 0;
355 int i;
356 StringAddrCounter *ssc;
357 for (ssc = *headP; ssc; ssc = ssc->next)
358 n_things++;
359 sortme = calloc(n_things, sizeof(foo));
360 n_things = 0;
361 for (ssc = *headP; ssc; ssc = ssc->next) {
362 sortme[n_things].cnt = ssc->count;
363 sortme[n_things].ptr = ssc;
364 n_things++;
365 }
366 qsort(sortme, n_things, sizeof(foo), foo_cmp);
367 for (i = 0; i < n_things; i++) {
368 *headP = sortme[i].ptr;
369 headP = &(*headP)->next;
370 }
371 free(sortme);
372 *headP = NULL;
373 }
375 #define RFC1035_MAXLABELSZ 63
376 static int
377 rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
378 )
379 {
380 off_t no = 0;
381 unsigned char c;
382 size_t len;
383 assert(ns > 0);
384 do {
385 if ((*off) >= sz)
386 break;
387 c = *(buf + (*off));
388 if (c > 191) {
389 /* blasted compression */
390 unsigned short s;
391 off_t ptr;
392 memcpy(&s, buf + (*off), sizeof(s));
393 s = ntohs(s);
394 (*off) += sizeof(s);
395 /* Sanity check */
396 if ((*off) >= sz)
397 return 1;
398 ptr = s & 0x3FFF;
399 /* Make sure the pointer is inside this message */
400 if (ptr >= sz)
401 return 2;
402 return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
403 } else if (c > RFC1035_MAXLABELSZ) {
404 /*
405 * "(The 10 and 01 combinations are reserved for future use.)"
406 */
407 break;
408 return 3;
409 } else {
410 (*off)++;
411 len = (size_t) c;
412 if (len == 0)
413 break;
414 if (len > (ns - 1))
415 len = ns - 1;
416 if ((*off) + len > sz) /* message is too short */
417 return 4;
418 memcpy(name + no, buf + (*off), len);
419 (*off) += len;
420 no += len;
421 *(name + (no++)) = '.';
422 }
423 } while (c > 0);
424 *(name + no - 1) = '\0';
425 /* make sure we didn't allow someone to overflow the name buffer */
426 assert(no <= ns);
427 return 0;
428 }
430 static const char *
431 QnameToNld(const char *qname, int nld)
432 {
433 const char *t = strrchr(qname, '.');
434 int dotcount = 1;
435 if (NULL == t)
436 t = qname;
437 if (0 == strcmp(t, ".arpa"))
438 dotcount--;
439 while (t > qname && dotcount < nld) {
440 t--;
441 if ('.' == *t)
442 dotcount++;
443 }
444 if (t > qname)
445 t++;
446 return t;
447 }
449 static int
450 handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
451 {
452 rfc1035_header qh;
453 unsigned short us;
454 char qname[MAX_QNAME_SZ];
455 unsigned short qtype;
456 unsigned short qclass;
457 off_t offset;
458 char *t;
459 const char *s;
460 int x;
461 StringCounter *sc;
462 StringAddrCounter *ssc;
464 if (len < sizeof(qh))
465 return 0;
467 memcpy(&us, buf + 00, 2);
468 qh.id = ntohs(us);
470 memcpy(&us, buf + 2, 2);
471 us = ntohs(us);
472 qh.qr = (us >> 15) & 0x01;
473 qh.opcode = (us >> 11) & 0x0F;
474 qh.aa = (us >> 10) & 0x01;
475 qh.tc = (us >> 9) & 0x01;
476 qh.rd = (us >> 8) & 0x01;
477 qh.ra = (us >> 7) & 0x01;
478 qh.rcode = us & 0x0F;
480 memcpy(&us, buf + 4, 2);
481 qh.qdcount = ntohs(us);
483 memcpy(&us, buf + 6, 2);
484 qh.ancount = ntohs(us);
486 memcpy(&us, buf + 8, 2);
487 qh.nscount = ntohs(us);
489 memcpy(&us, buf + 10, 2);
490 qh.arcount = ntohs(us);
492 offset = sizeof(qh);
493 memset(qname, '\0', MAX_QNAME_SZ);
494 x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ);
495 if (0 != x)
496 return 0;
497 if ('\0' == qname[0])
498 strcpy(qname, ".");
499 while ((t = strchr(qname, '\n')))
500 *t = ' ';
501 while ((t = strchr(qname, '\r')))
502 *t = ' ';
503 for (t = qname; *t; t++)
504 *t = tolower(*t);
506 memcpy(&us, buf + offset, 2);
507 qtype = ntohs(us);
508 memcpy(&us, buf + offset + 2, 2);
509 qclass = ntohs(us);
511 if (Filter && 0 == Filter(qtype, qclass, qname, sip, dip))
512 return 0;
514 /* gather stats */
515 qtype_counts[qtype]++;
516 qclass_counts[qclass]++;
517 opcode_counts[qh.opcode]++;
519 s = QnameToNld(qname, 1);
520 sc = StringCounter_lookup_or_add(&Tlds, s);
521 sc->count++;
523 if (sld_flag) {
524 s = QnameToNld(qname, 2);
525 sc = StringCounter_lookup_or_add(&Slds, s);
526 sc->count++;
528 /* increment StringAddrCounter */
529 ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
530 ssc->count++;
532 }
533 if (nld_flag) {
534 s = QnameToNld(qname, 3);
535 sc = StringCounter_lookup_or_add(&Nlds, s);
536 sc->count++;
538 /* increment StringAddrCounter */
539 ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
540 ssc->count++;
542 }
543 return 1;
544 }
546 static int
547 handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
548 {
549 char buf[PCAP_SNAPLEN];
550 if (port53 != udp->uh_dport)
551 return 0;
552 memcpy(buf, udp + 1, len - sizeof(*udp));
553 if (0 == handle_dns(buf, len - sizeof(*udp), sip, dip))
554 return 0;
555 return 1;
556 }
558 static int
559 handle_ip(const struct ip *ip, int len)
560 {
561 char buf[PCAP_SNAPLEN];
562 int offset = ip->ip_hl << 2;
563 AgentAddr *clt;
564 AgentAddr *srv;
565 if (ignore_addr.s_addr)
566 if (ip->ip_src.s_addr == ignore_addr.s_addr)
567 return 0;
568 if (IPPROTO_UDP != ip->ip_p)
569 return 0;
570 memcpy(buf, (void *) ip + offset, len - offset);
571 if (0 == handle_udp((struct udphdr *) buf, len - offset, ip->ip_src, ip->ip_dst))
572 return 0;
573 clt = AgentAddr_lookup_or_add(&Sources, ip->ip_src);
574 clt->count++;
575 srv = AgentAddr_lookup_or_add(&Destinations, ip->ip_dst);
576 srv->count++;
577 return 1;
578 }
580 #if USE_PPP
581 static int
582 handle_ppp(const u_char * pkt, int len)
583 {
584 char buf[PCAP_SNAPLEN];
585 unsigned short us;
586 unsigned short proto;
587 if (len < 2)
588 return 0;
589 if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
590 pkt += 2; /* ACFC not used */
591 len -= 2;
592 }
593 if (len < 2)
594 return 0;
595 if (*pkt % 2) {
596 proto = *pkt; /* PFC is used */
597 pkt++;
598 len--;
599 } else {
600 memcpy(&us, pkt, sizeof(us));
601 proto = ntohs(us);
602 pkt += 2;
603 len -= 2;
604 }
605 if (ETHERTYPE_IP != proto && PPP_IP != proto)
606 return 0;
607 memcpy(buf, pkt, len);
608 return handle_ip((struct ip *) buf, len);
609 }
611 #endif
613 static int
614 handle_null(const u_char * pkt, int len)
615 {
616 unsigned int family;
617 memcpy(&family, pkt, sizeof(family));
618 if (AF_INET != family)
619 return 0;
620 return handle_ip((struct ip *) (pkt + 4), len - 4);
621 }
623 #ifdef DLT_LOOP
624 static int
625 handle_loop(const u_char * pkt, int len)
626 {
627 unsigned int family;
628 memcpy(&family, pkt, sizeof(family));
629 if (AF_INET != ntohl(family))
630 return 0;
631 return handle_ip((struct ip *) (pkt + 4), len - 4);
632 }
634 #endif
636 #ifdef DLT_RAW
637 static int
638 handle_raw(const u_char * pkt, int len)
639 {
640 return handle_ip((struct ip *) pkt, len);
641 }
643 #endif
645 static int
646 handle_ether(const u_char * pkt, int len)
647 {
648 char buf[PCAP_SNAPLEN];
649 struct ether_header *e = (void *) pkt;
650 unsigned short etype = ntohs(e->ether_type);
651 if (len < ETHER_HDR_LEN)
652 return 0;
653 pkt += ETHER_HDR_LEN;
654 len -= ETHER_HDR_LEN;
655 if (ETHERTYPE_8021Q == etype) {
656 etype = ntohs(*(unsigned short *) (pkt + 2));
657 pkt += 4;
658 len -= 4;
659 }
660 if (ETHERTYPE_IP != etype)
661 return 0;
662 memcpy(buf, pkt, len);
663 return handle_ip((struct ip *) buf, len);
664 }
666 /* public function */
667 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
668 {
669 int status;
671 if (hdr->caplen < ETHER_HDR_LEN)
672 return;
674 switch (pcap_datalink (pcap))
675 {
676 case DLT_EN10MB:
677 status = handle_ether (pkt, hdr->caplen);
678 break;
679 #if USE_PPP
680 case DLT_PPP:
681 status = handle_ppp (pkt, hdr->caplen);
682 break;
683 #endif
684 #ifdef DLT_LOOP
685 case DLT_LOOP:
686 status = handle_loop (pkt, hdr->caplen);
687 break;
688 #endif
689 #ifdef DLT_RAW
690 case DLT_RAW:
691 status = handle_raw (pkt, hdr->caplen);
692 break;
693 #endif
694 case DLT_NULL:
695 status = handle_null (pkt, hdr->caplen);
696 break;
698 default:
699 fprintf (stderr, "unsupported data link type %d\n",
700 pcap_datalink(pcap));
701 status = 0;
702 break;
703 } /* switch (pcap_datalink(pcap)) */
705 if (0 == status)
706 return;
708 query_count_intvl++;
709 query_count_total++;
710 last_ts = hdr->ts;
711 }
713 static void
714 cron_pre(void)
715 {
716 AgentAddr_sort(&Sources);
717 AgentAddr_sort(&Destinations);
718 StringCounter_sort(&Tlds);
719 StringCounter_sort(&Slds);
720 StringCounter_sort(&Nlds);
721 StringAddrCounter_sort(&SSC2);
722 StringAddrCounter_sort(&SSC3);
723 }
725 static void
726 cron_post(void)
727 {
728 query_count_intvl = 0;
729 }
731 static void
732 keyboard(void)
733 {
734 int ch;
735 ch = getch() & 0xff;
736 if (ch >= 'A' && ch <= 'Z')
737 ch += 'a' - 'A';
738 switch (ch) {
739 case 's':
740 SubReport = Sources_report;
741 break;
742 case 'd':
743 SubReport = Destinatioreport;
744 break;
745 case '1':
746 SubReport = Tld_report;
747 break;
748 case '2':
749 SubReport = Sld_report;
750 break;
751 case '3':
752 SubReport = Nld_report;
753 break;
754 case 'c':
755 case '@':
756 SubReport = SldBySource_report;
757 break;
758 case '#':
759 SubReport = NldBySource_report;
760 break;
761 case 't':
762 SubReport = Qtypes_report;
763 break;
764 case 'o':
765 SubReport = Opcodes_report;
766 break;
767 case 030:
768 Quit = 1;
769 break;
770 case 022:
771 ResetCounters();
772 break;
773 case '?':
774 SubReport = Help_report;
775 break;
776 default:
777 break;
778 }
779 }
781 static void
782 Help_report(void)
783 {
784 print_func(" s - Sources list\n");
785 print_func(" d - Destinations list\n");
786 print_func(" t - Query types\n");
787 print_func(" o - Opcodes\n");
788 print_func(" 1 - TLD list\n");
789 print_func(" 2 - SLD list\n");
790 print_func(" 3 - 3LD list\n");
791 print_func(" @ - SLD+Sources list\n");
792 print_func(" # - 3LD+Sources list\n");
793 print_func("^R - Reset counters\n");
794 print_func("^X - Exit\n");
795 print_func("\n");
796 print_func("? - this\n");
797 }
799 static char *
800 qtype_str(int t)
801 {
802 static char buf[30];
803 switch (t) {
804 case T_A:
805 return "A?";
806 break;
807 case T_NS:
808 return "NS?";
809 break;
810 case T_CNAME:
811 return "CNAME?";
812 break;
813 case T_SOA:
814 return "SOA?";
815 break;
816 case T_PTR:
817 return "PTR?";
818 break;
819 case T_MX:
820 return "MX?";
821 break;
822 case T_TXT:
823 return "TXT?";
824 break;
825 case T_SIG:
826 return "SIG?";
827 break;
828 case T_KEY:
829 return "KEY?";
830 break;
831 case T_AAAA:
832 return "AAAA?";
833 break;
834 case T_LOC:
835 return "LOC?";
836 break;
837 case T_SRV:
838 return "SRV?";
839 break;
840 case T_A6:
841 return "A6?";
842 break;
843 case T_ANY:
844 return "ANY?";
845 break;
846 default:
847 snprintf(buf, 30, "#%d?", t);
848 return buf;
849 }
850 /* NOTREACHED */
851 }
853 static char *
854 opcode_str(int o)
855 {
856 static char buf[30];
857 switch (o) {
858 case 0:
859 return "Query";
860 break;
861 case 1:
862 return "Iquery";
863 break;
864 case 2:
865 return "Status";
866 break;
867 case 4:
868 return "Notify";
869 break;
870 case 5:
871 return "Update";
872 break;
873 default:
874 snprintf(buf, 30, "Opcode%d", o);
875 return buf;
876 }
877 /* NOTREACHED */
878 }
880 static int
881 get_nlines(void)
882 {
883 if (interactive)
884 return getmaxy(w) - 6;
885 else
886 return 50;
887 }
889 static void
890 StringCounter_report(StringCounter * list, char *what)
891 {
892 StringCounter *sc;
893 int nlines = get_nlines();
894 print_func("%-30s %9s %6s\n", what, "count", "%");
895 print_func("%-30s %9s %6s\n",
896 "------------------------------", "---------", "------");
897 for (sc = list; sc; sc = sc->next) {
898 print_func("%-30.30s %9d %6.1f\n",
899 sc->s,
900 sc->count,
901 100.0 * sc->count / query_count_total);
902 if (0 == --nlines)
903 break;
904 }
905 }
907 static void
908 StringCounter_free(StringCounter ** headP)
909 {
910 StringCounter *sc;
911 void *next;
912 for (sc = *headP; sc; sc = next) {
913 next = sc->next;
914 free(sc->s);
915 free(sc);
916 }
917 *headP = NULL;
918 }
920 static void
921 StringAddrCounter_free(StringAddrCounter ** headP)
922 {
923 StringAddrCounter *ssc;
924 void *next;
925 for (ssc = *headP; ssc; ssc = next) {
926 next = ssc->next;
927 free(ssc->str);
928 free(ssc);
929 }
930 *headP = NULL;
931 }
933 static void
934 Tld_report(void)
935 {
936 StringCounter_report(Tlds, "TLD");
937 }
939 static void
940 Sld_report(void)
941 {
942 if (0 == sld_flag) {
943 print_func("\tYou must start %s with the -s option\n", progname);
944 print_func("\tto collect 2nd level domain stats.\n", progname);
945 } else {
946 StringCounter_report(Slds, "SLD");
947 }
948 }
950 static void
951 Nld_report(void)
952 {
953 if (0 == nld_flag) {
954 print_func("\tYou must start %s with the -t option\n", progname);
955 print_func("\tto collect 3nd level domain stats.\n", progname);
956 } else {
957 StringCounter_report(Nlds, "3LD");
958 }
959 }
961 static void
962 Qtypes_report(void)
963 {
964 int type;
965 int nlines = get_nlines();
966 print_func("%-10s %9s %6s\n", "Query Type", "count", "%");
967 print_func("%-10s %9s %6s\n", "----------", "---------", "------");
968 for (type = 0; type < T_MAX; type++) {
969 if (0 == qtype_counts[type])
970 continue;
971 print_func("%-10s %9d %6.1f\n",
972 qtype_str(type),
973 qtype_counts[type],
974 100.0 * qtype_counts[type] / query_count_total);
975 if (0 == --nlines)
976 break;
977 }
978 }
980 static void
981 Opcodes_report(void)
982 {
983 int op;
984 int nlines = get_nlines();
985 print_func("%-10s %9s %6s\n", "Opcode ", "count", "%");
986 print_func("%-10s %9s %6s\n", "----------", "---------", "------");
987 for (op = 0; op < OP_MAX; op++) {
988 if (0 == opcode_counts[op])
989 continue;
990 print_func("%-10s %9d %6.1f\n",
991 opcode_str(op),
992 opcode_counts[op],
993 100.0 * opcode_counts[op] / query_count_total);
994 if (0 == --nlines)
995 break;
996 }
997 }
999 static void
1000 AgentAddr_report(AgentAddr * list, const char *what)
1001 {
1002 AgentAddr *agent;
1003 int nlines = get_nlines();
1004 print_func("%-16s %9s %6s\n", what, "count", "%");
1005 print_func("%-16s %9s %6s\n", "----------------", "---------", "------");
1006 for (agent = list; agent; agent = agent->next) {
1007 print_func("%-16s %9d %6.1f\n",
1008 anon_inet_ntoa(agent->src),
1009 agent->count,
1010 100.0 * agent->count / query_count_total);
1011 if (0 == --nlines)
1012 break;
1013 }
1014 }
1016 static void
1017 Combo_report(StringAddrCounter * list, char *what1, char *what2)
1018 {
1019 StringAddrCounter *ssc;
1020 int nlines = get_nlines();
1021 print_func("%-16s %-32s %9s %6s\n", what1, what2, "count", "%");
1022 print_func("%-16s %-32s %9s %6s\n",
1023 "----------------", "--------------------", "---------", "------");
1024 for (ssc = list; ssc; ssc = ssc->next) {
1025 print_func("%-16s %-32s %9d %6.1f\n",
1026 anon_inet_ntoa(ssc->src),
1027 ssc->str,
1028 ssc->count,
1029 100.0 * ssc->count / query_count_total);
1030 if (0 == --nlines)
1031 break;
1032 }
1033 }
1035 static void
1036 SldBySource_report(void)
1037 {
1038 if (0 == sld_flag) {
1039 print_func("\tYou must start %s with the -s option\n", progname);
1040 print_func("\tto collect 2nd level domain stats.\n", progname);
1041 } else {
1042 Combo_report(SSC2, "Source", "SLD");
1043 }
1044 }
1046 static void
1047 NldBySource_report(void)
1048 {
1049 if (0 == nld_flag) {
1050 print_func("\tYou must start %s with the -t option\n", progname);
1051 print_func("\tto collect 3nd level domain stats.\n", progname);
1052 } else {
1053 Combo_report(SSC3, "Source", "3LD");
1054 }
1055 }
1058 static void
1059 AgentAddr_free(AgentAddr ** headP)
1060 {
1061 AgentAddr *aa;
1062 void *next;
1063 for (aa = *headP; aa; aa = next) {
1064 next = aa->next;
1065 free(aa);
1066 }
1067 *headP = NULL;
1068 }
1070 static void
1071 Sources_report(void)
1072 {
1073 AgentAddr_report(Sources, "Sources");
1074 }
1076 static void
1077 Destinatioreport(void)
1078 {
1079 AgentAddr_report(Destinations, "Destinations");
1080 }
1082 static void
1083 report(void)
1084 {
1085 move(0, 0);
1086 print_func("%d new queries, %d total queries",
1087 query_count_intvl, query_count_total);
1088 clrtoeol();
1089 if (last_ts.tv_sec) {
1090 time_t t = (time_t) last_ts.tv_sec;
1091 move(0, 50);
1092 print_func("%s", ctime(&t));
1093 }
1094 move(2, 0);
1095 clrtobot();
1096 if (SubReport)
1097 SubReport();
1098 refresh();
1099 }
1101 /*
1102 * === BEGIN FILTERS ==========================================================
1103 */
1105 #include "known_tlds.h"
1107 static int
1108 UnknownTldFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1109 {
1110 const char *tld = QnameToNld(qn, 1);
1111 unsigned int i;
1112 if (NULL == tld)
1113 return 1; /* tld is unknown */
1114 for (i = 0; KnownTLDS[i]; i++)
1115 if (0 == strcmp(KnownTLDS[i], tld))
1116 return 0; /* tld is known */
1117 return 1; /* tld is unknown */
1118 }
1120 static int
1121 AforAFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1122 {
1123 struct in_addr a;
1124 if (qt != T_A)
1125 return 0;
1126 return inet_aton(qn, &a);
1127 }
1129 static int
1130 RFC1918PtrFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1131 {
1132 char *t;
1133 char q[128];
1134 unsigned int i = 0;
1135 if (qt != T_PTR)
1136 return 0;
1137 strncpy(q, qn, sizeof(q)-1);
1138 q[sizeof(q)-1] = '\0';
1139 t = strstr(q, ".in-addr.arpa");
1140 if (NULL == t)
1141 return 0;
1142 *t = '\0';
1143 for (t = strtok(q, "."); t; t = strtok(NULL, ".")) {
1144 i >>= 8;
1145 i |= ((atoi(t) & 0xff) << 24);
1146 }
1147 if ((i & 0xff000000) == 0x0a000000)
1148 return 1;
1149 if ((i & 0xfff00000) == 0xac100000)
1150 return 1;
1151 if ((i & 0xffff0000) == 0xc0a80000)
1152 return 1;
1153 return 0;
1154 }
1156 static void
1157 set_filter(const char *fn)
1158 {
1159 if (0 == strcmp(fn, "unknown-tlds"))
1160 Filter = UnknownTldFilter;
1161 else if (0 == strcmp(fn, "A-for-A"))
1162 Filter = AforAFilter;
1163 else if (0 == strcmp(fn, "rfc1918-ptr"))
1164 Filter = RFC1918PtrFilter;
1165 else
1166 Filter = NULL;
1167 }
1169 /*
1170 * === END FILTERS ==========================================================
1171 */
1173 static void
1174 init_curses(void)
1175 {
1176 w = initscr();
1177 cbreak();
1178 noecho();
1179 nodelay(w, 1);
1180 }
1182 static void
1183 ResetCounters(void)
1184 {
1185 query_count_intvl = 0;
1186 query_count_total = 0;
1187 memset(qtype_counts, '\0', sizeof(qtype_counts));
1188 memset(qclass_counts, '\0', sizeof(qclass_counts));
1189 memset(opcode_counts, '\0', sizeof(opcode_counts));
1190 AgentAddr_free(&Sources);
1191 AgentAddr_free(&Destinations);
1192 StringCounter_free(&Tlds);
1193 StringCounter_free(&Slds);
1194 StringCounter_free(&Nlds);
1195 StringAddrCounter_free(&SSC2);
1196 StringAddrCounter_free(&SSC3);
1197 memset(&last_ts, '\0', sizeof(last_ts));
1198 }
1200 static void
1201 usage(void)
1202 {
1203 fprintf(stderr, "usage: %s [opts] netdevice|savefile\n",
1204 progname);
1205 fprintf(stderr, "\t-a\tAnonymize IP Addrs\n");
1206 fprintf(stderr, "\t-b expr\tBPF program code\n");
1207 fprintf(stderr, "\t-i addr\tIgnore this source IP address\n");
1208 fprintf(stderr, "\t-p\tDon't put interface in promiscuous mode\n");
1209 fprintf(stderr, "\t-s\tEnable 2nd level domain stats collection\n");
1210 fprintf(stderr, "\t-t\tEnable 3nd level domain stats collection\n");
1211 fprintf(stderr, "\t-f\tfilter-name\n");
1212 fprintf(stderr, "\n");
1213 fprintf(stderr, "Available filters:\n");
1214 fprintf(stderr, "\tunknown-tlds\n");
1215 fprintf(stderr, "\tA-for-A\n");
1216 fprintf(stderr, "\trfc1918-ptr\n");
1217 exit(1);
1218 }
1220 static int
1221 pcap_select(pcap_t * p, int sec, int usec)
1222 {
1223 fd_set R;
1224 struct timeval to;
1225 FD_ZERO(&R);
1226 FD_SET(pcap_fileno(p), &R);
1227 to.tv_sec = sec;
1228 to.tv_usec = usec;
1229 return select(pcap_fileno(p) + 1, &R, NULL, NULL, &to);
1230 }
1232 #if 0
1233 static int
1234 main(int argc, char *argv[])
1235 {
1236 char errbuf[PCAP_ERRBUF_SIZE];
1237 int x;
1238 struct stat sb;
1239 int readfile_state = 0;
1240 struct bpf_program fp;
1242 port53 = htons(53);
1243 SubReport = Sources_report;
1244 ignore_addr.s_addr = 0;
1245 progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
1246 srandom(time(NULL));
1247 ResetCounters();
1249 while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
1250 switch (x) {
1251 case 'a':
1252 anon_flag = 1;
1253 break;
1254 case 's':
1255 sld_flag = 1;
1256 break;
1257 case 't':
1258 nld_flag = 1;
1259 break;
1260 case 'p':
1261 promisc_flag = 0;
1262 break;
1263 case 'b':
1264 bpf_program_str = strdup(optarg);
1265 break;
1266 case 'i':
1267 ignore_addr.s_addr = inet_addr(optarg);
1268 break;
1269 case 'f':
1270 set_filter(optarg);
1271 break;
1272 default:
1273 usage();
1274 break;
1275 }
1276 }
1277 argc -= optind;
1278 argv += optind;
1280 if (argc < 1)
1281 usage();
1282 device = strdup(argv[0]);
1284 if (0 == stat(device, &sb))
1285 readfile_state = 1;
1286 if (readfile_state) {
1287 pcap = pcap_open_offline(device, errbuf);
1288 } else {
1289 pcap = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
1290 }
1291 if (NULL == pcap) {
1292 fprintf(stderr, "pcap_open_*: %s\n", errbuf);
1293 exit(1);
1294 }
1296 if (0 == isatty(1)) {
1297 if (0 == readfile_state) {
1298 fprintf(stderr, "Non-interactive mode requires savefile argument\n");
1299 exit(1);
1300 }
1301 interactive = 0;
1302 print_func = printf;
1303 }
1305 memset(&fp, '\0', sizeof(fp));
1306 x = pcap_compile(pcap, &fp, bpf_program_str, 1, 0);
1307 if (x < 0) {
1308 fprintf(stderr, "pcap_compile failed\n");
1309 exit(1);
1310 }
1311 x = pcap_setfilter(pcap, &fp);
1312 if (x < 0) {
1313 fprintf(stderr, "pcap_setfilter failed\n");
1314 exit(1);
1315 }
1317 /*
1318 * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
1319 * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
1320 */
1321 x = pcap_setnonblock(pcap, 1, errbuf);
1322 if (x < 0) {
1323 fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
1324 exit(1);
1325 }
1327 switch (pcap_datalink(pcap)) {
1328 case DLT_EN10MB:
1329 handle_datalink = handle_ether;
1330 break;
1331 #if USE_PPP
1332 case DLT_PPP:
1333 handle_datalink = handle_ppp;
1334 break;
1335 #endif
1336 #ifdef DLT_LOOP
1337 case DLT_LOOP:
1338 handle_datalink = handle_loop;
1339 break;
1340 #endif
1341 #ifdef DLT_RAW
1342 case DLT_RAW:
1343 handle_datalink = handle_raw;
1344 break;
1345 #endif
1346 case DLT_NULL:
1347 handle_datalink = handle_null;
1348 break;
1349 default:
1350 fprintf(stderr, "unsupported data link type %d\n",
1351 pcap_datalink(pcap));
1352 return 1;
1353 break;
1354 }
1355 if (interactive) {
1356 init_curses();
1357 while (0 == Quit) {
1358 if (readfile_state < 2) {
1359 /*
1360 * On some OSes select() might return 0 even when
1361 * there are packets to process. Thus, we always
1362 * ignore its return value and just call pcap_dispatch()
1363 * anyway.
1364 */
1365 if (0 == readfile_state) /* interactive */
1366 pcap_select(pcap, 1, 0);
1367 x = pcap_dispatch(pcap, 50, handle_pcap, NULL);
1368 }
1369 if (0 == x && 1 == readfile_state) {
1370 /* block on keyboard until user quits */
1371 readfile_state++;
1372 nodelay(w, 0);
1373 }
1374 keyboard();
1375 cron_pre();
1376 report();
1377 cron_post();
1378 }
1379 endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
1380 } else {
1381 while (pcap_dispatch(pcap, 50, handle_pcap, NULL))
1382 (void) 0;
1383 cron_pre();
1384 Sources_report(); print_func("\n");
1385 Destinatioreport(); print_func("\n");
1386 Qtypes_report(); print_func("\n");
1387 Opcodes_report(); print_func("\n");
1388 Tld_report(); print_func("\n");
1389 Sld_report(); print_func("\n");
1390 Nld_report(); print_func("\n");
1391 SldBySource_report();
1392 }
1394 pcap_close(pcap);
1395 return 0;
1396 } /* static int main(int argc, char *argv[]) */
1397 #endif