Code

src/named.c: Fixed typos and stuff that prevented the compiler to build the file...
[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  */
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)
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;
237 static char *
238 anon_inet_ntoa(struct in_addr a)
240     if (anon_flag)
241         a = AnonMap_lookup_or_add(&Anons, a);
242     return inet_ntoa(a);
245 static AgentAddr *
246 AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
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);
257 static StringCounter *
258 StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
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);
269 static StringAddrCounter *
270 StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
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);
283 static int
284 foo_cmp(const void *A, const void *B)
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;
299 static void
300 AgentAddr_sort(AgentAddr ** headP)
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;
324 static void
325 StringCounter_sort(StringCounter ** headP)
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;
349 static void
350 StringAddrCounter_sort(StringAddrCounter ** headP)
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;
374 #define RFC1035_MAXLABELSZ 63
375 static int
376 rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
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;
429 static const char *
430 QnameToNld(const char *qname, int nld)
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;
448 static int
449 handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
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;
545 static int
546 handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
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;
557 static int
558 handle_ip(const struct ip *ip, int len)
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;
579 #if USE_PPP
580 static int
581 handle_ppp(const u_char * pkt, int len)
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);
610 #endif
612 static int
613 handle_null(const u_char * pkt, int len)
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);
622 #ifdef DLT_LOOP
623 static int
624 handle_loop(const u_char * pkt, int len)
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);
633 #endif
635 #ifdef DLT_RAW
636 static int
637 handle_raw(const u_char * pkt, int len)
639     return handle_ip((struct ip *) pkt, len);
642 #endif
644 static int
645 handle_ether(const u_char * pkt, int len)
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);
665 /* public function */
666 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
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;
712 static void
713 cron_pre(void)
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);
724 static void
725 cron_post(void)
727     query_count_intvl = 0;
730 static void
731 keyboard(void)
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     }
780 static void
781 Help_report(void)
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");
798 static char *
799 qtype_str(int t)
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 */
852 static char *
853 opcode_str(int o)
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 */
879 static int
880 get_nlines(void)
882         if (interactive)
883                 return getmaxy(w) - 6;
884         else
885                 return 50;
888 static void
889 StringCounter_report(StringCounter * list, char *what)
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     }
906 static void
907 StringCounter_free(StringCounter ** headP)
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;
919 static void
920 StringAddrCounter_free(StringAddrCounter ** headP)
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;
932 static void
933 Tld_report(void)
935     StringCounter_report(Tlds, "TLD");
938 static void
939 Sld_report(void)
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     }
949 static void
950 Nld_report(void)
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     }
960 static void
961 Qtypes_report(void)
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     }
979 static void
980 Opcodes_report(void)
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     }
998 static void
999 AgentAddr_report(AgentAddr * list, const char *what)
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     }
1015 static void
1016 Combo_report(StringAddrCounter * list, char *what1, char *what2)
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     }
1034 static void
1035 SldBySource_report(void)
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     }
1045 static void
1046 NldBySource_report(void)
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     }
1057 static void
1058 AgentAddr_free(AgentAddr ** headP)
1060     AgentAddr *aa;
1061     void *next;
1062     for (aa = *headP; aa; aa = next) {
1063         next = aa->next;
1064         free(aa);
1065     }
1066     *headP = NULL;
1069 static void
1070 Sources_report(void)
1072     AgentAddr_report(Sources, "Sources");
1075 static void
1076 Destinatioreport(void)
1078     AgentAddr_report(Destinations, "Destinations");
1081 static void
1082 report(void)
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();
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)
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 */
1119 static int
1120 AforAFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
1122     struct in_addr a;
1123     if (qt != T_A)
1124         return 0;  
1125     return inet_aton(qn, &a);
1128 static int
1129 RFC1918PtrFilter(unsigned short qt, unsigned short qc, const char *qn, const struct in_addr sip, const struct in_addr dip)
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;
1155 static void
1156 set_filter(const char *fn)
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;
1168 /*
1169  * === END FILTERS ==========================================================
1170  */
1172 static void
1173 init_curses(void)
1175     w = initscr();
1176     cbreak();
1177     noecho();
1178     nodelay(w, 1);
1181 static void
1182 ResetCounters(void)
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));
1199 static void
1200 usage(void)
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);
1219 static int
1220 pcap_select(pcap_t * p, int sec, int usec)
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);
1231 #if 0
1232 static int
1233 main(int argc, char *argv[])
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