Added a first rough draft of the new named plugin.
[collectd.git] / src / dnstop.c
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  */
35
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <sys/stat.h>
39
40 #include <netinet/in.h>
41
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
57
58 #include <sys/socket.h>
59 #include <net/if_arp.h>
60 #include <net/if.h>
61 #include <netinet/if_ether.h>
62
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <netinet/udp.h>
66
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
77
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
83
84 #ifdef __linux__
85 #define uh_dport dest
86 #endif
87
88 #include "dnstop.h"
89
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 };
99
100 typedef struct _StringCounter StringCounter;
101 struct _StringCounter {
102     char *s;
103     int count;
104     StringCounter *next;
105 };
106
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 };
115
116 typedef struct _foo foo;
117 struct _foo {
118     int cnt;
119     void *ptr;
120 };
121
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 };
137
138 typedef struct _AnonMap AnonMap;
139 struct _AnonMap {
140     struct in_addr real;
141     struct in_addr anon;
142     AnonMap *next;
143 };
144
145 typedef int Filter_t(unsigned short,
146         unsigned short,
147         const char *,
148         const struct in_addr,
149         const struct in_addr);
150
151 typedef int (printer)(const char *, ...);
152
153 /*
154  * flags/features for non-interactive mode
155  */
156
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
166
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;
186
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
204
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);
217
218 static Filter_t UnknownTldFilter;
219 static Filter_t AforAFilter;
220 static Filter_t RFC1918PtrFilter;
221 static Filter_t *Filter = NULL;
222
223 static printer *print_func = (printer *) printw;
224
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 }
237
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 }
245
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 }
257
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 }
269
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 }
283
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 }
299
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 }
324
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 }
349
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 }
374
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 }
429
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 }
448
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;
463
464     if (len < sizeof(qh))
465         return 0;
466
467     memcpy(&us, buf + 00, 2);
468     qh.id = ntohs(us);
469
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;
479
480     memcpy(&us, buf + 4, 2);
481     qh.qdcount = ntohs(us);
482
483     memcpy(&us, buf + 6, 2);
484     qh.ancount = ntohs(us);
485
486     memcpy(&us, buf + 8, 2);
487     qh.nscount = ntohs(us);
488
489     memcpy(&us, buf + 10, 2);
490     qh.arcount = ntohs(us);
491
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);
505
506     memcpy(&us, buf + offset, 2);
507     qtype = ntohs(us);
508     memcpy(&us, buf + offset + 2, 2);
509     qclass = ntohs(us);
510
511     if (Filter && 0 == Filter(qtype, qclass, qname, sip, dip))
512         return 0;
513
514     /* gather stats */
515     qtype_counts[qtype]++;
516     qclass_counts[qclass]++;
517     opcode_counts[qh.opcode]++;
518
519     s = QnameToNld(qname, 1);
520     sc = StringCounter_lookup_or_add(&Tlds, s);
521     sc->count++;
522
523     if (sld_flag) {
524         s = QnameToNld(qname, 2);
525         sc = StringCounter_lookup_or_add(&Slds, s);
526         sc->count++;
527
528         /* increment StringAddrCounter */
529         ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
530         ssc->count++;
531
532     }
533     if (nld_flag) {
534         s = QnameToNld(qname, 3);
535         sc = StringCounter_lookup_or_add(&Nlds, s);
536         sc->count++;
537
538         /* increment StringAddrCounter */
539         ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
540         ssc->count++;
541
542     }
543     return 1;
544 }
545
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 }
557
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 }
579
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 }
610
611 #endif
612
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 }
622
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 }
633
634 #endif
635
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 }
642
643 #endif
644
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 }
665
666 /* public function */
667 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
668 {
669     int status;
670
671     if (hdr->caplen < ETHER_HDR_LEN)
672         return;
673
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;
697
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)) */
704
705     if (0 == status)
706         return;
707
708     query_count_intvl++;
709     query_count_total++;
710     last_ts = hdr->ts;
711 }
712
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 }
724
725 static void
726 cron_post(void)
727 {
728     query_count_intvl = 0;
729 }
730
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 }
780
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 }
798
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 }
852
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 }
879
880 static int
881 get_nlines(void)
882 {
883         if (interactive)
884                 return getmaxy(w) - 6;
885         else
886                 return 50;
887 }
888
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 }
906
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 }
919
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 }
932
933 static void
934 Tld_report(void)
935 {
936     StringCounter_report(Tlds, "TLD");
937 }
938
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 }
949
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 }
960
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 }
979
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 }
998
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 }
1015
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 }
1034
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 }
1045
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 }
1056
1057
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 }
1069
1070 static void
1071 Sources_report(void)
1072 {
1073     AgentAddr_report(Sources, "Sources");
1074 }
1075
1076 static void
1077 Destinatioreport(void)
1078 {
1079     AgentAddr_report(Destinations, "Destinations");
1080 }
1081
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 }
1100
1101 /*
1102  * === BEGIN FILTERS ==========================================================
1103  */
1104
1105 #include "known_tlds.h"
1106
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 }
1119
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 }
1128
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 }
1155
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 }
1168
1169 /*
1170  * === END FILTERS ==========================================================
1171  */
1172
1173 static void
1174 init_curses(void)
1175 {
1176     w = initscr();
1177     cbreak();
1178     noecho();
1179     nodelay(w, 1);
1180 }
1181
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 }
1199
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 }
1219
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 }
1231
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;
1241
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();
1248
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;
1279
1280     if (argc < 1)
1281         usage();
1282     device = strdup(argv[0]);
1283
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     }
1295
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     }
1304
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     }
1316
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     }
1326
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     }
1393
1394     pcap_close(pcap);
1395     return 0;
1396 } /* static int main(int argc, char *argv[]) */
1397 #endif