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 #ifndef T_A6
158 #define T_A6 38
159 #endif
160 #ifndef T_SRV
161 #define T_SRV 33
162 #endif
163 #define C_MAX 65536
164 #define OP_MAX 16
166 /*
167 * Global variables
168 */
169 static int interactive = 1;
170 static char *device = NULL;
171 static struct in_addr ignore_addr;
172 static pcap_t *pcap = NULL;
173 static char *bpf_program_str = "udp dst port 53 and udp[10:2] & 0x8000 = 0";
174 static WINDOW *w;
175 static unsigned short port53;
176 static void (*SubReport) (void) = NULL;
177 static int (*handle_datalink) (const u_char * pkt, int len) = NULL;
178 static int Quit = 0;
179 static char *progname = NULL;
180 static int anon_flag = 0;
181 static int sld_flag = 0;
182 static int nld_flag = 0;
183 static int promisc_flag = 1;
184 static AnonMap *Anons = NULL;
186 static int query_count_intvl = 0;
187 static int query_count_total = 0;
188 int qtype_counts[T_MAX];
189 static int opcode_counts[OP_MAX];
190 static int qclass_counts[C_MAX];
191 static AgentAddr *Sources = NULL;
192 static AgentAddr *Destinations = NULL;
193 static StringCounter *Tlds = NULL;
194 static StringCounter *Slds = NULL;
195 static StringCounter *Nlds = NULL;
196 static StringAddrCounter *SSC2 = NULL;
197 static StringAddrCounter *SSC3 = NULL;
198 #ifdef __OpenBSD__
199 static struct bpf_timeval last_ts;
200 #else
201 static struct timeval last_ts;
202 #endif
204 /* Prototypes */
205 static void SldBySource_report(void);
206 static void NldBySource_report(void);
207 static void Sources_report(void);
208 static void Destinatioreport(void);
209 static void Qtypes_report(void);
210 static void Opcodes_report(void);
211 static void Tld_report(void);
212 static void Sld_report(void);
213 static void Nld_report(void);
214 static void Help_report(void);
215 static void ResetCounters(void);
217 static Filter_t UnknownTldFilter;
218 static Filter_t AforAFilter;
219 static Filter_t RFC1918PtrFilter;
220 static Filter_t *Filter = NULL;
222 static printer *print_func = (printer *) printw;
224 static struct in_addr
225 AnonMap_lookup_or_add(AnonMap ** headP, struct in_addr real)
226 {
227 AnonMap **T;
228 for (T = headP; (*T); T = &(*T)->next)
229 if ((*T)->real.s_addr == real.s_addr)
230 return (*T)->anon;
231 (*T) = calloc(1, sizeof(**T));
232 (*T)->real = real;
233 (*T)->anon.s_addr = random();
234 return (*T)->anon;
235 }
237 static char *
238 anon_inet_ntoa(struct in_addr a)
239 {
240 if (anon_flag)
241 a = AnonMap_lookup_or_add(&Anons, a);
242 return inet_ntoa(a);
243 }
245 static AgentAddr *
246 AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
247 {
248 AgentAddr **T;
249 for (T = headP; (*T); T = &(*T)->next)
250 if ((*T)->src.s_addr == a.s_addr)
251 return (*T);
252 (*T) = calloc(1, sizeof(**T));
253 (*T)->src = a;
254 return (*T);
255 }
257 static StringCounter *
258 StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
259 {
260 StringCounter **T;
261 for (T = headP; (*T); T = &(*T)->next)
262 if (0 == strcmp((*T)->s, s))
263 return (*T);
264 (*T) = calloc(1, sizeof(**T));
265 (*T)->s = strdup(s);
266 return (*T);
267 }
269 static StringAddrCounter *
270 StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
271 {
272 StringAddrCounter **T;
273 for (T = headP; (*T); T = &(*T)->next)
274 if (0 == strcmp((*T)->str, str))
275 if ((*T)->src.s_addr == a.s_addr)
276 return (*T);
277 (*T) = calloc(1, sizeof(**T));
278 (*T)->str = strdup(str);
279 (*T)->src = a;
280 return (*T);
281 }
283 static int
284 foo_cmp(const void *A, const void *B)
285 {
286 const foo *a = A;
287 const foo *b = B;
288 if (a->cnt < b->cnt)
289 return 1;
290 if (a->cnt > b->cnt)
291 return -1;
292 if (a->ptr < b->ptr)
293 return 1;
294 if (a->ptr > b->ptr)
295 return -1;
296 return 0;
297 }
299 static void
300 AgentAddr_sort(AgentAddr ** headP)
301 {
302 foo *sortme;
303 int n_agents = 0;
304 int i;
305 AgentAddr *a;
306 for (a = *headP; a; a = a->next)
307 n_agents++;
308 sortme = calloc(n_agents, sizeof(foo));
309 n_agents = 0;
310 for (a = *headP; a; a = a->next) {
311 sortme[n_agents].cnt = a->count;
312 sortme[n_agents].ptr = a;
313 n_agents++;
314 }
315 qsort(sortme, n_agents, sizeof(foo), foo_cmp);
316 for (i = 0; i < n_agents; i++) {
317 *headP = sortme[i].ptr;
318 headP = &(*headP)->next;
319 }
320 free(sortme);
321 *headP = NULL;
322 }
324 static void
325 StringCounter_sort(StringCounter ** headP)
326 {
327 foo *sortme;
328 int n_things = 0;
329 int i;
330 StringCounter *sc;
331 for (sc = *headP; sc; sc = sc->next)
332 n_things++;
333 sortme = calloc(n_things, sizeof(foo));
334 n_things = 0;
335 for (sc = *headP; sc; sc = sc->next) {
336 sortme[n_things].cnt = sc->count;
337 sortme[n_things].ptr = sc;
338 n_things++;
339 }
340 qsort(sortme, n_things, sizeof(foo), foo_cmp);
341 for (i = 0; i < n_things; i++) {
342 *headP = sortme[i].ptr;
343 headP = &(*headP)->next;
344 }
345 free(sortme);
346 *headP = NULL;
347 }
349 static void
350 StringAddrCounter_sort(StringAddrCounter ** headP)
351 {
352 foo *sortme;
353 int n_things = 0;
354 int i;
355 StringAddrCounter *ssc;
356 for (ssc = *headP; ssc; ssc = ssc->next)
357 n_things++;
358 sortme = calloc(n_things, sizeof(foo));
359 n_things = 0;
360 for (ssc = *headP; ssc; ssc = ssc->next) {
361 sortme[n_things].cnt = ssc->count;
362 sortme[n_things].ptr = ssc;
363 n_things++;
364 }
365 qsort(sortme, n_things, sizeof(foo), foo_cmp);
366 for (i = 0; i < n_things; i++) {
367 *headP = sortme[i].ptr;
368 headP = &(*headP)->next;
369 }
370 free(sortme);
371 *headP = NULL;
372 }
374 #define RFC1035_MAXLABELSZ 63
375 static int
376 rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
377 )
378 {
379 off_t no = 0;
380 unsigned char c;
381 size_t len;
382 assert(ns > 0);
383 do {
384 if ((*off) >= sz)
385 break;
386 c = *(buf + (*off));
387 if (c > 191) {
388 /* blasted compression */
389 unsigned short s;
390 off_t ptr;
391 memcpy(&s, buf + (*off), sizeof(s));
392 s = ntohs(s);
393 (*off) += sizeof(s);
394 /* Sanity check */
395 if ((*off) >= sz)
396 return 1;
397 ptr = s & 0x3FFF;
398 /* Make sure the pointer is inside this message */
399 if (ptr >= sz)
400 return 2;
401 return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
402 } else if (c > RFC1035_MAXLABELSZ) {
403 /*
404 * "(The 10 and 01 combinations are reserved for future use.)"
405 */
406 break;
407 return 3;
408 } else {
409 (*off)++;
410 len = (size_t) c;
411 if (len == 0)
412 break;
413 if (len > (ns - 1))
414 len = ns - 1;
415 if ((*off) + len > sz) /* message is too short */
416 return 4;
417 memcpy(name + no, buf + (*off), len);
418 (*off) += len;
419 no += len;
420 *(name + (no++)) = '.';
421 }
422 } while (c > 0);
423 *(name + no - 1) = '\0';
424 /* make sure we didn't allow someone to overflow the name buffer */
425 assert(no <= ns);
426 return 0;
427 }
429 static const char *
430 QnameToNld(const char *qname, int nld)
431 {
432 const char *t = strrchr(qname, '.');
433 int dotcount = 1;
434 if (NULL == t)
435 t = qname;
436 if (0 == strcmp(t, ".arpa"))
437 dotcount--;
438 while (t > qname && dotcount < nld) {
439 t--;
440 if ('.' == *t)
441 dotcount++;
442 }
443 if (t > qname)
444 t++;
445 return t;
446 }
448 static int
449 handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
450 {
451 rfc1035_header qh;
452 unsigned short us;
453 char qname[MAX_QNAME_SZ];
454 unsigned short qtype;
455 unsigned short qclass;
456 off_t offset;
457 char *t;
458 const char *s;
459 int x;
460 StringCounter *sc;
461 StringAddrCounter *ssc;
463 if (len < sizeof(qh))
464 return 0;
466 memcpy(&us, buf + 00, 2);
467 qh.id = ntohs(us);
469 memcpy(&us, buf + 2, 2);
470 us = ntohs(us);
471 qh.qr = (us >> 15) & 0x01;
472 qh.opcode = (us >> 11) & 0x0F;
473 qh.aa = (us >> 10) & 0x01;
474 qh.tc = (us >> 9) & 0x01;
475 qh.rd = (us >> 8) & 0x01;
476 qh.ra = (us >> 7) & 0x01;
477 qh.rcode = us & 0x0F;
479 memcpy(&us, buf + 4, 2);
480 qh.qdcount = ntohs(us);
482 memcpy(&us, buf + 6, 2);
483 qh.ancount = ntohs(us);
485 memcpy(&us, buf + 8, 2);
486 qh.nscount = ntohs(us);
488 memcpy(&us, buf + 10, 2);
489 qh.arcount = ntohs(us);
491 offset = sizeof(qh);
492 memset(qname, '\0', MAX_QNAME_SZ);
493 x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ);
494 if (0 != x)
495 return 0;
496 if ('\0' == qname[0])
497 strcpy(qname, ".");
498 while ((t = strchr(qname, '\n')))
499 *t = ' ';
500 while ((t = strchr(qname, '\r')))
501 *t = ' ';
502 for (t = qname; *t; t++)
503 *t = tolower(*t);
505 memcpy(&us, buf + offset, 2);
506 qtype = ntohs(us);
507 memcpy(&us, buf + offset + 2, 2);
508 qclass = ntohs(us);
510 if (Filter && 0 == Filter(qtype, qclass, qname, sip, dip))
511 return 0;
513 /* gather stats */
514 qtype_counts[qtype]++;
515 qclass_counts[qclass]++;
516 opcode_counts[qh.opcode]++;
518 s = QnameToNld(qname, 1);
519 sc = StringCounter_lookup_or_add(&Tlds, s);
520 sc->count++;
522 if (sld_flag) {
523 s = QnameToNld(qname, 2);
524 sc = StringCounter_lookup_or_add(&Slds, s);
525 sc->count++;
527 /* increment StringAddrCounter */
528 ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
529 ssc->count++;
531 }
532 if (nld_flag) {
533 s = QnameToNld(qname, 3);
534 sc = StringCounter_lookup_or_add(&Nlds, s);
535 sc->count++;
537 /* increment StringAddrCounter */
538 ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
539 ssc->count++;
541 }
542 return 1;
543 }
545 static int
546 handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
547 {
548 char buf[PCAP_SNAPLEN];
549 if (port53 != udp->uh_dport)
550 return 0;
551 memcpy(buf, udp + 1, len - sizeof(*udp));
552 if (0 == handle_dns(buf, len - sizeof(*udp), sip, dip))
553 return 0;
554 return 1;
555 }
557 static int
558 handle_ip(const struct ip *ip, int len)
559 {
560 char buf[PCAP_SNAPLEN];
561 int offset = ip->ip_hl << 2;
562 AgentAddr *clt;
563 AgentAddr *srv;
564 if (ignore_addr.s_addr)
565 if (ip->ip_src.s_addr == ignore_addr.s_addr)
566 return 0;
567 if (IPPROTO_UDP != ip->ip_p)
568 return 0;
569 memcpy(buf, (void *) ip + offset, len - offset);
570 if (0 == handle_udp((struct udphdr *) buf, len - offset, ip->ip_src, ip->ip_dst))
571 return 0;
572 clt = AgentAddr_lookup_or_add(&Sources, ip->ip_src);
573 clt->count++;
574 srv = AgentAddr_lookup_or_add(&Destinations, ip->ip_dst);
575 srv->count++;
576 return 1;
577 }
579 #if USE_PPP
580 static int
581 handle_ppp(const u_char * pkt, int len)
582 {
583 char buf[PCAP_SNAPLEN];
584 unsigned short us;
585 unsigned short proto;
586 if (len < 2)
587 return 0;
588 if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
589 pkt += 2; /* ACFC not used */
590 len -= 2;
591 }
592 if (len < 2)
593 return 0;
594 if (*pkt % 2) {
595 proto = *pkt; /* PFC is used */
596 pkt++;
597 len--;
598 } else {
599 memcpy(&us, pkt, sizeof(us));
600 proto = ntohs(us);
601 pkt += 2;
602 len -= 2;
603 }
604 if (ETHERTYPE_IP != proto && PPP_IP != proto)
605 return 0;
606 memcpy(buf, pkt, len);
607 return handle_ip((struct ip *) buf, len);
608 }
610 #endif
612 static int
613 handle_null(const u_char * pkt, int len)
614 {
615 unsigned int family;
616 memcpy(&family, pkt, sizeof(family));
617 if (AF_INET != family)
618 return 0;
619 return handle_ip((struct ip *) (pkt + 4), len - 4);
620 }
622 #ifdef DLT_LOOP
623 static int
624 handle_loop(const u_char * pkt, int len)
625 {
626 unsigned int family;
627 memcpy(&family, pkt, sizeof(family));
628 if (AF_INET != ntohl(family))
629 return 0;
630 return handle_ip((struct ip *) (pkt + 4), len - 4);
631 }
633 #endif
635 #ifdef DLT_RAW
636 static int
637 handle_raw(const u_char * pkt, int len)
638 {
639 return handle_ip((struct ip *) pkt, len);
640 }
642 #endif
644 static int
645 handle_ether(const u_char * pkt, int len)
646 {
647 char buf[PCAP_SNAPLEN];
648 struct ether_header *e = (void *) pkt;
649 unsigned short etype = ntohs(e->ether_type);
650 if (len < ETHER_HDR_LEN)
651 return 0;
652 pkt += ETHER_HDR_LEN;
653 len -= ETHER_HDR_LEN;
654 if (ETHERTYPE_8021Q == etype) {
655 etype = ntohs(*(unsigned short *) (pkt + 2));
656 pkt += 4;
657 len -= 4;
658 }
659 if (ETHERTYPE_IP != etype)
660 return 0;
661 memcpy(buf, pkt, len);
662 return handle_ip((struct ip *) buf, len);
663 }
665 /* public function */
666 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
667 {
668 int status;
670 if (hdr->caplen < ETHER_HDR_LEN)
671 return;
673 switch (pcap_datalink (pcap))
674 {
675 case DLT_EN10MB:
676 status = handle_ether (pkt, hdr->caplen);
677 break;
678 #if USE_PPP
679 case DLT_PPP:
680 status = handle_ppp (pkt, hdr->caplen);
681 break;
682 #endif
683 #ifdef DLT_LOOP
684 case DLT_LOOP:
685 status = handle_loop (pkt, hdr->caplen);
686 break;
687 #endif
688 #ifdef DLT_RAW
689 case DLT_RAW:
690 status = handle_raw (pkt, hdr->caplen);
691 break;
692 #endif
693 case DLT_NULL:
694 status = handle_null (pkt, hdr->caplen);
695 break;
697 default:
698 fprintf (stderr, "unsupported data link type %d\n",
699 pcap_datalink(pcap));
700 status = 0;
701 break;
702 } /* switch (pcap_datalink(pcap)) */
704 if (0 == status)
705 return;
707 query_count_intvl++;
708 query_count_total++;
709 last_ts = hdr->ts;
710 }
712 static void
713 cron_pre(void)
714 {
715 AgentAddr_sort(&Sources);
716 AgentAddr_sort(&Destinations);
717 StringCounter_sort(&Tlds);
718 StringCounter_sort(&Slds);
719 StringCounter_sort(&Nlds);
720 StringAddrCounter_sort(&SSC2);
721 StringAddrCounter_sort(&SSC3);
722 }
724 static void
725 cron_post(void)
726 {
727 query_count_intvl = 0;
728 }
730 static void
731 keyboard(void)
732 {
733 int ch;
734 ch = getch() & 0xff;
735 if (ch >= 'A' && ch <= 'Z')
736 ch += 'a' - 'A';
737 switch (ch) {
738 case 's':
739 SubReport = Sources_report;
740 break;
741 case 'd':
742 SubReport = Destinatioreport;
743 break;
744 case '1':
745 SubReport = Tld_report;
746 break;
747 case '2':
748 SubReport = Sld_report;
749 break;
750 case '3':
751 SubReport = Nld_report;
752 break;
753 case 'c':
754 case '@':
755 SubReport = SldBySource_report;
756 break;
757 case '#':
758 SubReport = NldBySource_report;
759 break;
760 case 't':
761 SubReport = Qtypes_report;
762 break;
763 case 'o':
764 SubReport = Opcodes_report;
765 break;
766 case 030:
767 Quit = 1;
768 break;
769 case 022:
770 ResetCounters();
771 break;
772 case '?':
773 SubReport = Help_report;
774 break;
775 default:
776 break;
777 }
778 }
780 static void
781 Help_report(void)
782 {
783 print_func(" s - Sources list\n");
784 print_func(" d - Destinations list\n");
785 print_func(" t - Query types\n");
786 print_func(" o - Opcodes\n");
787 print_func(" 1 - TLD list\n");
788 print_func(" 2 - SLD list\n");
789 print_func(" 3 - 3LD list\n");
790 print_func(" @ - SLD+Sources list\n");
791 print_func(" # - 3LD+Sources list\n");
792 print_func("^R - Reset counters\n");
793 print_func("^X - Exit\n");
794 print_func("\n");
795 print_func("? - this\n");
796 }
798 static char *
799 qtype_str(int t)
800 {
801 static char buf[30];
802 switch (t) {
803 case T_A:
804 return "A?";
805 break;
806 case T_NS:
807 return "NS?";
808 break;
809 case T_CNAME:
810 return "CNAME?";
811 break;
812 case T_SOA:
813 return "SOA?";
814 break;
815 case T_PTR:
816 return "PTR?";
817 break;
818 case T_MX:
819 return "MX?";
820 break;
821 case T_TXT:
822 return "TXT?";
823 break;
824 case T_SIG:
825 return "SIG?";
826 break;
827 case T_KEY:
828 return "KEY?";
829 break;
830 case T_AAAA:
831 return "AAAA?";
832 break;
833 case T_LOC:
834 return "LOC?";
835 break;
836 case T_SRV:
837 return "SRV?";
838 break;
839 case T_A6:
840 return "A6?";
841 break;
842 case T_ANY:
843 return "ANY?";
844 break;
845 default:
846 snprintf(buf, 30, "#%d?", t);
847 return buf;
848 }
849 /* NOTREACHED */
850 }
852 static char *
853 opcode_str(int o)
854 {
855 static char buf[30];
856 switch (o) {
857 case 0:
858 return "Query";
859 break;
860 case 1:
861 return "Iquery";
862 break;
863 case 2:
864 return "Status";
865 break;
866 case 4:
867 return "Notify";
868 break;
869 case 5:
870 return "Update";
871 break;
872 default:
873 snprintf(buf, 30, "Opcode%d", o);
874 return buf;
875 }
876 /* NOTREACHED */
877 }
879 static int
880 get_nlines(void)
881 {
882 if (interactive)
883 return getmaxy(w) - 6;
884 else
885 return 50;
886 }
888 static void
889 StringCounter_report(StringCounter * list, char *what)
890 {
891 StringCounter *sc;
892 int nlines = get_nlines();
893 print_func("%-30s %9s %6s\n", what, "count", "%");
894 print_func("%-30s %9s %6s\n",
895 "------------------------------", "---------", "------");
896 for (sc = list; sc; sc = sc->next) {
897 print_func("%-30.30s %9d %6.1f\n",
898 sc->s,
899 sc->count,
900 100.0 * sc->count / query_count_total);
901 if (0 == --nlines)
902 break;
903 }
904 }
906 static void
907 StringCounter_free(StringCounter ** headP)
908 {
909 StringCounter *sc;
910 void *next;
911 for (sc = *headP; sc; sc = next) {
912 next = sc->next;
913 free(sc->s);
914 free(sc);
915 }
916 *headP = NULL;
917 }
919 static void
920 StringAddrCounter_free(StringAddrCounter ** headP)
921 {
922 StringAddrCounter *ssc;
923 void *next;
924 for (ssc = *headP; ssc; ssc = next) {
925 next = ssc->next;
926 free(ssc->str);
927 free(ssc);
928 }
929 *headP = NULL;
930 }
932 static void
933 Tld_report(void)
934 {
935 StringCounter_report(Tlds, "TLD");
936 }
938 static void
939 Sld_report(void)
940 {
941 if (0 == sld_flag) {
942 print_func("\tYou must start %s with the -s option\n", progname);
943 print_func("\tto collect 2nd level domain stats.\n", progname);
944 } else {
945 StringCounter_report(Slds, "SLD");
946 }
947 }
949 static void
950 Nld_report(void)
951 {
952 if (0 == nld_flag) {
953 print_func("\tYou must start %s with the -t option\n", progname);
954 print_func("\tto collect 3nd level domain stats.\n", progname);
955 } else {
956 StringCounter_report(Nlds, "3LD");
957 }
958 }
960 static void
961 Qtypes_report(void)
962 {
963 int type;
964 int nlines = get_nlines();
965 print_func("%-10s %9s %6s\n", "Query Type", "count", "%");
966 print_func("%-10s %9s %6s\n", "----------", "---------", "------");
967 for (type = 0; type < T_MAX; type++) {
968 if (0 == qtype_counts[type])
969 continue;
970 print_func("%-10s %9d %6.1f\n",
971 qtype_str(type),
972 qtype_counts[type],
973 100.0 * qtype_counts[type] / query_count_total);
974 if (0 == --nlines)
975 break;
976 }
977 }
979 static void
980 Opcodes_report(void)
981 {
982 int op;
983 int nlines = get_nlines();
984 print_func("%-10s %9s %6s\n", "Opcode ", "count", "%");
985 print_func("%-10s %9s %6s\n", "----------", "---------", "------");
986 for (op = 0; op < OP_MAX; op++) {
987 if (0 == opcode_counts[op])
988 continue;
989 print_func("%-10s %9d %6.1f\n",
990 opcode_str(op),
991 opcode_counts[op],
992 100.0 * opcode_counts[op] / query_count_total);
993 if (0 == --nlines)
994 break;
995 }
996 }
998 static void
999 AgentAddr_report(AgentAddr * list, const char *what)
1000 {
1001 AgentAddr *agent;
1002 int nlines = get_nlines();
1003 print_func("%-16s %9s %6s\n", what, "count", "%");
1004 print_func("%-16s %9s %6s\n", "----------------", "---------", "------");
1005 for (agent = list; agent; agent = agent->next) {
1006 print_func("%-16s %9d %6.1f\n",
1007 anon_inet_ntoa(agent->src),
1008 agent->count,
1009 100.0 * agent->count / query_count_total);
1010 if (0 == --nlines)
1011 break;
1012 }
1013 }
1015 static void
1016 Combo_report(StringAddrCounter * list, char *what1, char *what2)
1017 {
1018 StringAddrCounter *ssc;
1019 int nlines = get_nlines();
1020 print_func("%-16s %-32s %9s %6s\n", what1, what2, "count", "%");
1021 print_func("%-16s %-32s %9s %6s\n",
1022 "----------------", "--------------------", "---------", "------");
1023 for (ssc = list; ssc; ssc = ssc->next) {
1024 print_func("%-16s %-32s %9d %6.1f\n",
1025 anon_inet_ntoa(ssc->src),
1026 ssc->str,
1027 ssc->count,
1028 100.0 * ssc->count / query_count_total);
1029 if (0 == --nlines)
1030 break;
1031 }
1032 }
1034 static void
1035 SldBySource_report(void)
1036 {
1037 if (0 == sld_flag) {
1038 print_func("\tYou must start %s with the -s option\n", progname);
1039 print_func("\tto collect 2nd level domain stats.\n", progname);
1040 } else {
1041 Combo_report(SSC2, "Source", "SLD");
1042 }
1043 }
1045 static void
1046 NldBySource_report(void)
1047 {
1048 if (0 == nld_flag) {
1049 print_func("\tYou must start %s with the -t option\n", progname);
1050 print_func("\tto collect 3nd level domain stats.\n", progname);
1051 } else {
1052 Combo_report(SSC3, "Source", "3LD");
1053 }
1054 }
1057 static void
1058 AgentAddr_free(AgentAddr ** headP)
1059 {
1060 AgentAddr *aa;
1061 void *next;
1062 for (aa = *headP; aa; aa = next) {
1063 next = aa->next;
1064 free(aa);
1065 }
1066 *headP = NULL;
1067 }
1069 static void
1070 Sources_report(void)
1071 {
1072 AgentAddr_report(Sources, "Sources");
1073 }
1075 static void
1076 Destinatioreport(void)
1077 {
1078 AgentAddr_report(Destinations, "Destinations");
1079 }
1081 static void
1082 report(void)
1083 {
1084 move(0, 0);
1085 print_func("%d new queries, %d total queries",
1086 query_count_intvl, query_count_total);
1087 clrtoeol();
1088 if (last_ts.tv_sec) {
1089 time_t t = (time_t) last_ts.tv_sec;
1090 move(0, 50);
1091 print_func("%s", ctime(&t));
1092 }
1093 move(2, 0);
1094 clrtobot();
1095 if (SubReport)
1096 SubReport();
1097 refresh();
1098 }
1100 /*
1101 * === BEGIN FILTERS ==========================================================
1102 */
1104 #include "known_tlds.h"
1106 static int
1107 UnknownTldFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1108 {
1109 const char *tld = QnameToNld(qn, 1);
1110 unsigned int i;
1111 if (NULL == tld)
1112 return 1; /* tld is unknown */
1113 for (i = 0; KnownTLDS[i]; i++)
1114 if (0 == strcmp(KnownTLDS[i], tld))
1115 return 0; /* tld is known */
1116 return 1; /* tld is unknown */
1117 }
1119 static int
1120 AforAFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1121 {
1122 struct in_addr a;
1123 if (qt != T_A)
1124 return 0;
1125 return inet_aton(qn, &a);
1126 }
1128 static int
1129 RFC1918PtrFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1130 {
1131 char *t;
1132 char q[128];
1133 unsigned int i = 0;
1134 if (qt != T_PTR)
1135 return 0;
1136 strncpy(q, qn, sizeof(q)-1);
1137 q[sizeof(q)-1] = '\0';
1138 t = strstr(q, ".in-addr.arpa");
1139 if (NULL == t)
1140 return 0;
1141 *t = '\0';
1142 for (t = strtok(q, "."); t; t = strtok(NULL, ".")) {
1143 i >>= 8;
1144 i |= ((atoi(t) & 0xff) << 24);
1145 }
1146 if ((i & 0xff000000) == 0x0a000000)
1147 return 1;
1148 if ((i & 0xfff00000) == 0xac100000)
1149 return 1;
1150 if ((i & 0xffff0000) == 0xc0a80000)
1151 return 1;
1152 return 0;
1153 }
1155 static void
1156 set_filter(const char *fn)
1157 {
1158 if (0 == strcmp(fn, "unknown-tlds"))
1159 Filter = UnknownTldFilter;
1160 else if (0 == strcmp(fn, "A-for-A"))
1161 Filter = AforAFilter;
1162 else if (0 == strcmp(fn, "rfc1918-ptr"))
1163 Filter = RFC1918PtrFilter;
1164 else
1165 Filter = NULL;
1166 }
1168 /*
1169 * === END FILTERS ==========================================================
1170 */
1172 static void
1173 init_curses(void)
1174 {
1175 w = initscr();
1176 cbreak();
1177 noecho();
1178 nodelay(w, 1);
1179 }
1181 static void
1182 ResetCounters(void)
1183 {
1184 query_count_intvl = 0;
1185 query_count_total = 0;
1186 memset(qtype_counts, '\0', sizeof(qtype_counts));
1187 memset(qclass_counts, '\0', sizeof(qclass_counts));
1188 memset(opcode_counts, '\0', sizeof(opcode_counts));
1189 AgentAddr_free(&Sources);
1190 AgentAddr_free(&Destinations);
1191 StringCounter_free(&Tlds);
1192 StringCounter_free(&Slds);
1193 StringCounter_free(&Nlds);
1194 StringAddrCounter_free(&SSC2);
1195 StringAddrCounter_free(&SSC3);
1196 memset(&last_ts, '\0', sizeof(last_ts));
1197 }
1199 static void
1200 usage(void)
1201 {
1202 fprintf(stderr, "usage: %s [opts] netdevice|savefile\n",
1203 progname);
1204 fprintf(stderr, "\t-a\tAnonymize IP Addrs\n");
1205 fprintf(stderr, "\t-b expr\tBPF program code\n");
1206 fprintf(stderr, "\t-i addr\tIgnore this source IP address\n");
1207 fprintf(stderr, "\t-p\tDon't put interface in promiscuous mode\n");
1208 fprintf(stderr, "\t-s\tEnable 2nd level domain stats collection\n");
1209 fprintf(stderr, "\t-t\tEnable 3nd level domain stats collection\n");
1210 fprintf(stderr, "\t-f\tfilter-name\n");
1211 fprintf(stderr, "\n");
1212 fprintf(stderr, "Available filters:\n");
1213 fprintf(stderr, "\tunknown-tlds\n");
1214 fprintf(stderr, "\tA-for-A\n");
1215 fprintf(stderr, "\trfc1918-ptr\n");
1216 exit(1);
1217 }
1219 static int
1220 pcap_select(pcap_t * p, int sec, int usec)
1221 {
1222 fd_set R;
1223 struct timeval to;
1224 FD_ZERO(&R);
1225 FD_SET(pcap_fileno(p), &R);
1226 to.tv_sec = sec;
1227 to.tv_usec = usec;
1228 return select(pcap_fileno(p) + 1, &R, NULL, NULL, &to);
1229 }
1231 #if 0
1232 static int
1233 main(int argc, char *argv[])
1234 {
1235 char errbuf[PCAP_ERRBUF_SIZE];
1236 int x;
1237 struct stat sb;
1238 int readfile_state = 0;
1239 struct bpf_program fp;
1241 port53 = htons(53);
1242 SubReport = Sources_report;
1243 ignore_addr.s_addr = 0;
1244 progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
1245 srandom(time(NULL));
1246 ResetCounters();
1248 while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
1249 switch (x) {
1250 case 'a':
1251 anon_flag = 1;
1252 break;
1253 case 's':
1254 sld_flag = 1;
1255 break;
1256 case 't':
1257 nld_flag = 1;
1258 break;
1259 case 'p':
1260 promisc_flag = 0;
1261 break;
1262 case 'b':
1263 bpf_program_str = strdup(optarg);
1264 break;
1265 case 'i':
1266 ignore_addr.s_addr = inet_addr(optarg);
1267 break;
1268 case 'f':
1269 set_filter(optarg);
1270 break;
1271 default:
1272 usage();
1273 break;
1274 }
1275 }
1276 argc -= optind;
1277 argv += optind;
1279 if (argc < 1)
1280 usage();
1281 device = strdup(argv[0]);
1283 if (0 == stat(device, &sb))
1284 readfile_state = 1;
1285 if (readfile_state) {
1286 pcap = pcap_open_offline(device, errbuf);
1287 } else {
1288 pcap = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
1289 }
1290 if (NULL == pcap) {
1291 fprintf(stderr, "pcap_open_*: %s\n", errbuf);
1292 exit(1);
1293 }
1295 if (0 == isatty(1)) {
1296 if (0 == readfile_state) {
1297 fprintf(stderr, "Non-interactive mode requires savefile argument\n");
1298 exit(1);
1299 }
1300 interactive = 0;
1301 print_func = printf;
1302 }
1304 memset(&fp, '\0', sizeof(fp));
1305 x = pcap_compile(pcap, &fp, bpf_program_str, 1, 0);
1306 if (x < 0) {
1307 fprintf(stderr, "pcap_compile failed\n");
1308 exit(1);
1309 }
1310 x = pcap_setfilter(pcap, &fp);
1311 if (x < 0) {
1312 fprintf(stderr, "pcap_setfilter failed\n");
1313 exit(1);
1314 }
1316 /*
1317 * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
1318 * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
1319 */
1320 x = pcap_setnonblock(pcap, 1, errbuf);
1321 if (x < 0) {
1322 fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
1323 exit(1);
1324 }
1326 switch (pcap_datalink(pcap)) {
1327 case DLT_EN10MB:
1328 handle_datalink = handle_ether;
1329 break;
1330 #if USE_PPP
1331 case DLT_PPP:
1332 handle_datalink = handle_ppp;
1333 break;
1334 #endif
1335 #ifdef DLT_LOOP
1336 case DLT_LOOP:
1337 handle_datalink = handle_loop;
1338 break;
1339 #endif
1340 #ifdef DLT_RAW
1341 case DLT_RAW:
1342 handle_datalink = handle_raw;
1343 break;
1344 #endif
1345 case DLT_NULL:
1346 handle_datalink = handle_null;
1347 break;
1348 default:
1349 fprintf(stderr, "unsupported data link type %d\n",
1350 pcap_datalink(pcap));
1351 return 1;
1352 break;
1353 }
1354 if (interactive) {
1355 init_curses();
1356 while (0 == Quit) {
1357 if (readfile_state < 2) {
1358 /*
1359 * On some OSes select() might return 0 even when
1360 * there are packets to process. Thus, we always
1361 * ignore its return value and just call pcap_dispatch()
1362 * anyway.
1363 */
1364 if (0 == readfile_state) /* interactive */
1365 pcap_select(pcap, 1, 0);
1366 x = pcap_dispatch(pcap, 50, handle_pcap, NULL);
1367 }
1368 if (0 == x && 1 == readfile_state) {
1369 /* block on keyboard until user quits */
1370 readfile_state++;
1371 nodelay(w, 0);
1372 }
1373 keyboard();
1374 cron_pre();
1375 report();
1376 cron_post();
1377 }
1378 endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
1379 } else {
1380 while (pcap_dispatch(pcap, 50, handle_pcap, NULL))
1381 (void) 0;
1382 cron_pre();
1383 Sources_report(); print_func("\n");
1384 Destinatioreport(); print_func("\n");
1385 Qtypes_report(); print_func("\n");
1386 Opcodes_report(); print_func("\n");
1387 Tld_report(); print_func("\n");
1388 Sld_report(); print_func("\n");
1389 Nld_report(); print_func("\n");
1390 SldBySource_report();
1391 }
1393 pcap_close(pcap);
1394 return 0;
1395 } /* static int main(int argc, char *argv[]) */
1396 #endif