1 /********************************************************************************
2 
3 	Program: dns_flood_detector.c
4 	 Author: Dennis Opacki <dopacki@adotout.com>
5 	   Date: Tue Mar 18 16:46:53 EST 2003
6 	Purpose: Monitor DNS servers for abusive usage levels
7 		 and alarm to syslog
8 
9 	compile with:
10 	gcc -o dns_flood_detector -lpcap -lpthread -lm dns_flood_detector.c
11 
12 	command-line options:
13 
14 	-i ifname	specify interface to listen on (default lets pcap pick)
15 	-t n		alarm when more than n queries per second are observed
16 			(default 40)
17 	-a n		wait for n seconds before alarming again on same source
18 			(default 90)
19 	-w n		calculate statistics every n seconds
20 			(default 10)
21 	-x n		use n buckets
22 			(default 50)
23 	-m n		mark overall query rate every n seconds
24 			(default disabled)
25 	-A addr		filter for specific address
26 	-M mask		netmask for filter (in conjunction with -A)
27 	-Q		monitor any addresses (default is to filter only for
28 			primary addresses on chosen interface)
29 	-b		run in foreground in "bindsnap" mode
30 	-d		run in background in "daemon" mode
31 	-D		dump dns packets (implies -b)
32 	-v		detailed information (use twice for more detail)
33         -s              send source IP stats to collector as JSON
34         -z N.N.N.N      address to send stats to (default 226.1.1.2)
35         -p N            UDP port to send stats to (default 2000)
36 
37 	-h		usage info
38 
39     Copyright (C) 2003  Dennis Opacki
40 
41     This program is free software; you can redistribute it and/or modify
42     it under the terms of the GNU General Public License as published by
43     the Free Software Foundation; either version 2 of the License, or
44     (at your option) any later version.
45 
46     This program is distributed in the hope that it will be useful,
47     but WITHOUT ANY WARRANTY; without even the implied warranty of
48     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
49     GNU General Public License for more details.
50 
51     You should have received a copy of the GNU General Public License
52     along with this program; if not, write to the Free Software
53     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
54 
55     --- new in v1.05 ---
56     8/18/2003 - FreeBSD target - Jim Westfall <jwestfall@surrealistic.net>
57     8/18/2003 - Moved to getopt(3) for compatibility <dopacki@adotout.com>
58     8/19/2003 - Added OSX/BSDI make targets - <dopacki@adotout.com>
59                 Added ability to specify inteface - <dopacki@adotout.com>
60 
61     --- new in v1.06 ---
62     8/20/2003 - Added Solaris9 make target - <dopacki@adotout.com>
63     8/26/2003 - Fixed tcp qdcount bug - <dopacki@adotout.com>
64 
65     --- new in v1.07 ---
66     8/27/2003 - Fixed alarm reset bug - <dopacki@adotout.com>
67     8/28/2003 - Added malloc_fail function - <dopacki@adotout.com>
68     8/28/2003 - Added mutex thread locking - <dopacki@adotout.com>
69     8/30/2003 - Fixed wierd qtype segfault - <jwestfall@surrealistic.net>
70 					     <dopacki@adotout.com>
71 
72     --- new in v1.08 ---
73     9/02/2003 - Added -v -v output in daemon mode - <dopacki@adotout.com>
74 
75     --- new in v1.09 ---
76     10/19/2003 - Added stdout flushing to bindsnap mode - <dopacki@adotout.com>
77     10/19/2003 - Changed logging priority to LOG_NOTICE - <dopacki@adotout.com>
78     10/19/2003 - Fixed low traffic verbose logging bugs - <dopacki@adotout.com>
79 
80     --- new in v1.10 ---
81     10/22/2003 - Added 'mark status' option via '-m' - <dopacki@adotout.com>
82     10/23/2003 - Code cleanup in verbose syslogging - <dopacki@adotout.com>
83 
84     --- new in v1.11 ---
85     06/14/2005 - added A6, AAAA, ANY qtypes - <jwestfall@surrealistic.net>
86                  examine all packets with >= 1 qdcount - <jwestfall@surrealistic.net>
87                  stop processing packet if invalid dns char - <jwestfall@surrealistic.net>
88                  fix tcp parsing - <jwestfall@surrealistic.net>
89                  add option_D to dump packets - <jwestfall@surrealistic.net>
90 
91     --- new in v1.12 ---
92     03/03/2006 - added address filtering options - <erikm@buh.org>
93                  fix segfault using argv[0] after getopt - <erikm@buh.org>
94                  fix rounding from float/int conversions, use unsigned more consistently - <erikm@buh.org>
95                  clean up to work with -Wall - <erikm@buh.org>
96                  show fractional qps rates for totals - <erikm@buh.org>
97                  store addresses raw, instead of as text (speedup/reduce memory usage) - <erikm@buh.org>
98                  fix crash on long syslog messages - <jwestfall@surrealistic.net>
99 
100     --- new in v1.2 ---
101     05/10/2012 - added sending of source-IP telemetry to a network collector - <dopacki@adotout.com>
102 
103 ********************************************************************************/
104 
105 #include <pcap.h>
106 #include <stdio.h>
107 #include <stdlib.h>
108 #include <fcntl.h>
109 #include <errno.h>
110 #include <netinet/in_systm.h>
111 #include <netinet/in.h>
112 #include <netinet/ip.h>
113 #include <netinet/tcp.h>
114 #include <netinet/udp.h>
115 #include <arpa/inet.h>
116 #ifdef __bsdi__
117 #include <net/if_ethernet.h>
118 #else
119 #ifdef __sun__
120 #include <sys/ethernet.h>
121 #else
122 #include <net/ethernet.h>
123 #endif
124 #endif
125 #include <pthread.h>
126 #include <unistd.h>
127 #include <time.h>
128 #include <math.h>
129 #include <signal.h>
130 #include <syslog.h>
131 #include <string.h>
132 #include <sys/stat.h>
133 #include "dns_flood_detector.h"
134 #include <sys/types.h>
135 #include <sys/socket.h>
136 #include <sys/select.h>
137 
138 // global variables and their defaults
139 pthread_mutex_t stats_lock;
140 struct bucket **bb;
141 int option_t = 60;
142 int option_a = 90;
143 int option_w = 20;
144 int option_x = 10000;
145 int option_m = 0;
146 int option_b = 0;
147 int option_d = 0;
148 int option_D = 0;
149 int option_v = 0;
150 int option_h = 0;
151 int option_Q = 0;
152 int option_A = 0;
153 int option_M = 0;
154 int option_s = 0;
155 int totals = 0;
156 
157 static char *target_ip = NULL;
158 int target_port = DEFAULT_PORT;
159 int mcast_ttl = 10;
160 char hostname[HOST_NAME_MAX];
161 
162 char VERSION[] = "1.2";
163 
164 // 255.255.255.255 is invalid as a src IP address; we'll use it to mark empty buckets
165 #define BCAST 0xffFFffFF
166 
167 // this is our statistics thread
run_stats()168 void *run_stats () {
169   // prepare multicast socket
170   struct sockaddr_in addr;
171   int sock;
172   sock = socket(AF_INET, SOCK_DGRAM,0);
173 
174   if  (sock<0) {
175     perror("can't set up socket");
176     exit(1);
177   }
178 
179   // is it harmful to set this on non-multicast sockets?
180   setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&mcast_ttl, sizeof(mcast_ttl));
181 
182   bzero((char*)&addr, sizeof(addr));
183   addr.sin_family = AF_INET;
184   addr.sin_addr.s_addr = inet_addr(target_ip);
185   addr.sin_port = htons(target_port);
186 
187   // get our hostname
188   gethostname(hostname, HOST_NAME_MAX-1);
189 
190   while (1) {
191 
192     // check statistical stuff
193     pthread_mutex_lock(&stats_lock);
194     calculate_averages();
195     saddr_stats(sock,addr,hostname);
196     pthread_mutex_unlock(&stats_lock);
197     sleep (option_w);
198   }
199 }
200 
201 // report saddr stats
saddr_stats(int sock,struct sockaddr_in addr,char * hostname)202 int saddr_stats(int sock, struct sockaddr_in addr, char *hostname) {
203   u_int i;
204   int addrlen;
205   char buff[MAXMESSAGE];
206   int buffhead = 0;
207   char st_time[10];
208   time_t now = time(0);
209   struct tm *raw_time = localtime(&now);
210   addrlen = sizeof(addr);
211   snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
212 
213   // prepare jason structure for multicast datagrams
214   char head[MAXHEAD];
215   char *tail = "}}";
216   snprintf(head,MAXHEAD,"{\"hostname\":\"%s\",\"type\":\"source\",\"data\":{",hostname);
217   int netsize = MAXMESSAGE - strlen(head) - strlen(tail);
218   if (netsize<=0) exit(EXIT_FAILURE); // this should never ever happen
219 
220   int avail = netsize;
221   int dlen = 0;
222   char datalet[MAXDATALET];
223 
224   // copy the initial json header into the buffer
225   bzero(buff,sizeof(buff));
226   memcpy(buff,head,strlen(head));
227   buffhead = buffhead + strlen(head);
228 
229   // report all source address stats, cleaning up afterward
230   for (i=0; ( (i < option_x) && ( bb[i]->ip_addr.s_addr != 0 ) ); i++) {
231 
232     if ( bb[i]->ip_addr.s_addr != BCAST ) {
233 
234       // prepare a datalet
235       snprintf(datalet,MAXDATALET,"\"%s\":%d,",inet_ntoa(bb[i]->ip_addr),bb[i]->udp_count+bb[i]->tcp_count);
236       dlen = strlen(datalet);
237 
238       // see if the current datagram has room for the datalet
239       if ( avail > dlen ) {
240 
241         // append this datalet to the current datagram (minus null terminator)
242         avail = avail - dlen;
243         memcpy(buff+buffhead,datalet,dlen);
244         buffhead = buffhead + dlen;
245       }
246       // no room in current dgram
247       else {
248 
249         // remove trailing comma from the buffer so we can close it out
250         buffhead = buffhead - 1;
251 
252         // add the tail
253         strncpy(buff+buffhead,tail, strlen(tail));
254 
255         // send the transmission if option_s is set
256         if (option_s > 0 ) {
257           sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
258           microsleep(10);
259         }
260 
261         // init next datagram
262         bzero(buff,sizeof(buff));
263         memcpy(buff,head,strlen(head));
264         buffhead = strlen(head);
265         avail = netsize;
266 
267         // append this datalet to the current datagram (minus null terminatin)
268         avail = avail - dlen;
269         memcpy(buff+buffhead,datalet,dlen);
270         buffhead = buffhead + dlen;
271       }
272     }
273 
274     scour_bucket(i);
275   }
276 
277   // transmit final buffer contents if needed
278   if ( ( option_b == 0) && (buffhead>strlen(head)) ) {
279 
280     // remove trailing comma
281     buffhead = buffhead - 1;
282 
283     // add the tail
284     strncpy(buff+buffhead,tail,strlen(tail));
285 
286     // send the multicast transmission
287     sendto(sock,buff,strlen(buff)+1,0,(struct sockaddr *) &addr, addrlen);
288     microsleep(10);
289 
290     bzero(buff,sizeof(buff));
291   }
292   return 1;
293 }
294 
295 
296 // calculate the running average within each bucket
calculate_averages()297 int calculate_averages() {
298   u_int i,j,delta,cursize,qps;
299   int newsize;
300   float qpsf;
301   char st_time[10];
302   time_t now = time(0);
303   u_int types[] = {1,2,5,6,12,15,28,38,252,255,0};
304   char *target;
305   char *names[] = {"A","NS","CNAME","SOA","PTR","MX","AAAA","A6","AXFR","ANY",""};
306   struct tm *raw_time = localtime(&now);
307   snprintf(st_time, 9, "%02d:%02d:%02d",raw_time->tm_hour,raw_time->tm_min,raw_time->tm_sec);
308 
309   for (i=0; i<option_x; i++) {
310 
311     // only process valid buckets
312     if ( bb[i]->ip_addr.s_addr != BCAST) {
313       delta = now - bb[i]->first_packet;
314 
315       // let's try to avoid a divide-by-zero, shall we?
316       if (delta > 1 ) {
317 
318         // round our average and save it in the bucket
319         bb[i]->qps = (u_int)ceil( (bb[i]->tcp_count + bb[i]->udp_count) / (float)delta);
320 
321         // handle threshold crossing
322         if ( bb[i]->qps > option_t ) {
323 
324           // display detail to either syslog or stdout
325           if ( option_b ) {
326             if ( ! option_v ) {
327               printf("[%s] source [%s] - %u qps\n",st_time,inet_ntoa(bb[i]->ip_addr),bb[i]->qps);
328               fflush(stdout);
329             }
330             else {
331               printf("[%s] source [%s] - %u qps tcp : %u qps udp ",st_time,inet_ntoa(bb[i]->ip_addr),
332               (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
333               (u_int)ceil( ((float)bb[i]->udp_count/delta)));
334 
335               if ( option_v >1 ) {
336                 for (j=0;types[j];j++) {
337                   qps = (u_int)ceil((float)bb[i]->qstats[types[j]]/delta);
338                   if (qps){
339                     printf("[%u qps %s] ",qps,names[j]);
340                   }
341                 }
342               }
343               printf("\n");
344               fflush(stdout);
345             }
346           }
347           else {
348             // if running in background, use alarm reset timer
349             if ((now-bb[i]->alarm_set)>option_a) {
350 
351               // display appropriate level of detail via syslog
352               if ( ! option_v ) {
353                 syslog(LOG_NOTICE,"source [%s] - %u qps\n",inet_ntoa(bb[i]->ip_addr),bb[i]->qps);
354               }
355               else if (option_v > 1) {
356                 target = (char *)malloc(sizeof(char)*MAXSYSLOG);
357                 newsize = MAXSYSLOG;
358                 cursize = snprintf(target,newsize,"source [%s] - %u tcp qps : %u udp qps ",inet_ntoa(bb[i]->ip_addr),
359                 (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
360                 (u_int)ceil( ((float)bb[i]->udp_count/delta)));
361                 newsize-=cursize;
362 
363                 for (j=0;types[j];j++ ) {
364                   qps = (u_int)ceil(((float)bb[i]->qstats[types[j]]/delta));
365                   if ( ( qps > 0)  && ( newsize > 1 ) ) {
366                     cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%u qps %s] ",qps,names[j]);
367                     newsize-=cursize;
368                   }
369                 }
370                 if (newsize <= 0 ) {
371                   target[MAXSYSLOG-1]='\0';
372                 }
373                 syslog(LOG_NOTICE,"%s",target);
374                 free(target);
375               }
376               else {
377                 syslog(LOG_NOTICE,"source [%s] - %u tcp qps - %u udp qps\n",inet_ntoa(bb[i]->ip_addr),
378                 (u_int)ceil( ((float)bb[i]->tcp_count/delta)),
379                 (u_int)ceil( ((float)bb[i]->udp_count/delta)));
380               }
381 
382               // reset alarm
383               bb[i]->alarm_set = now;
384             }
385           }
386         }
387       }
388     }
389   }
390 
391   // 'mark stats' if required and it is time
392   delta = (u_int)(now - bb[totals]->first_packet);
393   if ( (option_m > 0)&&(delta > 1)&&(delta >= option_m) ) {
394 
395     // handle bindsnap mode
396     if (option_b) {
397       printf("[%s] totals - %3.2f qps tcp : %3.2f qps udp ",st_time, ((float)bb[totals]->tcp_count/delta),((float)bb[totals]->udp_count/delta));
398       if (option_v) {
399         for (j=0;types[j];j++) {
400           qpsf = ((float)bb[totals]->qstats[types[j]]/delta);
401           if (qpsf > 0){
402             printf("[%3.2f qps %s] ",qpsf,names[j]);
403           }
404         }
405       }
406       printf("\n");
407       fflush(stdout);
408     }
409     else {
410       // agonizing high verbosity code
411       if (option_v) {
412         target = (char *)malloc(sizeof(char)*MAXSYSLOG);
413         newsize = MAXSYSLOG;
414         cursize = snprintf(target,newsize,"[totals] - %3.2f tcp qps : %3.2f udp qps ",
415         ((float)bb[totals]->tcp_count/delta),
416         ((float)bb[totals]->udp_count/delta));
417         newsize-=cursize;
418 
419         for (j=0;types[j];j++ ) {
420           qpsf = ((float)bb[totals]->qstats[types[j]]/delta);
421           if ( ( qpsf > 0)  && ( newsize > 1 ) ) {
422             cursize = snprintf(target+(MAXSYSLOG-newsize),newsize,"[%3.2f qps %s] ",qpsf,names[j]);
423             newsize-=cursize;
424           }
425         }
426         if (newsize <= 0 ) {
427           target[MAXSYSLOG-1]='\0';
428         }
429         syslog(LOG_NOTICE,"%s",target);
430         free(target);
431       }
432       else {
433         syslog(LOG_NOTICE,"[totals] - %3.2f tcp qps : %3.2f udp qps\n",
434         ((float)bb[totals]->tcp_count/delta),
435         ((float)bb[totals]->udp_count/delta));
436       }
437     }
438     scour_bucket(totals);
439   }
440   return 1;
441 }
442 
valid_dns_char(char c)443 int valid_dns_char(char c) {
444 
445   if((c >= '0' && c <= '9')
446   || (c >= 'a' && c <= 'z')
447   || (c >= 'A' && c <= 'Z')
448   || (c == '-')
449   || (c == '_')) // is valid for SRV records.
450     return 1;
451 
452   return 0;
453 }
454 
455 // purge and initialize all buckets
init_buckets()456 void init_buckets() {
457 	u_int i;
458 
459   // create bucket brigade (final bucket is for totals)
460   pthread_mutex_lock(&stats_lock);
461   if ( ( bb = malloc( sizeof(struct bucket *) * (option_x+1)) ) == NULL ) malloc_fail("bb", sizeof(struct bucket *) * (option_x+1));
462   for (i=0; i <=option_x; i++ ) {
463     if ( ( bb[i] = (struct bucket *)malloc( sizeof(struct bucket) ) ) == NULL) malloc_fail("bb[i]", sizeof(struct bucket) );
464     scour_bucket(i);
465   }
466   pthread_mutex_unlock(&stats_lock);
467 }
468 
469 // clean out a bucket while avoiding obvious memory leak
scour_bucket(int i)470 int scour_bucket( int i ) {
471   int j;
472 
473   bb[i]->ip_addr.s_addr=BCAST;
474   bb[i]->tcp_count=0;
475   bb[i]->udp_count=0;
476   bb[i]->qps=0;
477   bb[i]->first_packet=time(0);
478   bb[i]->last_packet=(time_t)0;
479   bb[i]->alarm_set=(time_t)0;
480 
481   for (j=0;j<256;j++) {
482     bb[i]->qstats[j]=0;
483   }
484 
485   return 1;
486 }
487 
488 // add a packet to a bucket
add_to_bucket(struct in_addr * ip_src,int ip_proto,int num_queries,u_int8_t qtype)489 int add_to_bucket ( struct in_addr *ip_src, int ip_proto, int num_queries, u_int8_t qtype) {
490   int bucket = 0;
491 
492   // get the bucket to put packet in
493   pthread_mutex_lock(&stats_lock);
494   bucket = find_bucket(ip_src);
495 
496   // set bucket fields
497   bb[bucket]->last_packet = time(0);
498   if (ip_proto == 6 ) {
499     bb[bucket]->tcp_count+=num_queries;
500     bb[totals]->tcp_count+=num_queries;
501   }
502   else {
503     bb[bucket]->udp_count+=num_queries;
504     bb[totals]->udp_count+=num_queries;
505   }
506 
507   bb[bucket]->qstats[qtype]+=num_queries;
508   bb[totals]->qstats[qtype]+=num_queries;
509   pthread_mutex_unlock(&stats_lock);
510 
511   return 1;
512 }
513 
514 // figure out where to put this packet
find_bucket(struct in_addr * ip_src)515 int find_bucket(struct in_addr *ip_src) {
516   int i, bucket=0;
517   time_t oldest=0;
518 
519   // look for an existing bucket for this IP
520   for (i=0; i< option_x; i++ ){
521     // ip field of bucket seems to match the ip we are checking
522     if (bb[i]->ip_addr.s_addr == ip_src->s_addr) {
523       return i;
524     }
525   }
526 
527   // look for unused buckets
528   for (i=0; i< option_x; i++ ) {
529 
530     // found an unused one - clean it, init it, and return it
531     if ( bb[i]->ip_addr.s_addr == BCAST ) {
532       scour_bucket(i);
533       bb[i]->ip_addr.s_addr = ip_src->s_addr;
534       return i;
535     }
536 
537     // find the most stagnant bucket in case we need it
538     // avoids another loop through the buckets
539     // TODO - should we autoflush buckets after some idle time,
540     //        or after alarming?  fixes the case where
541     //        alarms are unlikely to reappear even if a client
542     //        resumes flooding if there isn't bucket contention
543     //        churning them out and resetting the timer for the rate
544     //        calculation...
545 
546     if ( ( bb[i]->last_packet != 0 ) && ((oldest==0)||( bb[i]->last_packet < oldest))) {
547       oldest = bb[i]->last_packet;
548       bucket = i;
549     }
550   }
551 
552   // use the most stagnant bucket since all are in use
553   // clean it, init it, and return it
554   scour_bucket(bucket);
555   bb[i]->ip_addr.s_addr = ip_src->s_addr;
556 
557   return bucket;
558 }
559 
560 // handle all packets we throw at it
handle_IP(u_char * args,const struct pcap_pkthdr * pkthdr,const u_char * packet)561 void handle_IP(u_char *args, const struct pcap_pkthdr* pkthdr,const u_char* packet){
562   const struct ip* ip;
563   const struct my_dns *dns;
564   const struct tcphdr *tcp;
565   const struct udphdr *udp;
566   u_int length = pkthdr->len;
567   u_int caplen = pkthdr->caplen;
568   u_int hlen,off,version;
569   unsigned char dname[NS_MAXDNAME]="";
570   struct in_addr ip_src;
571   unsigned char *data;
572   u_int len,dpos;
573   u_int8_t qtype,tlen;
574 
575   // skip the ethernet header
576   length -= sizeof(struct ether_header);
577 
578   // make sure packet is a valid length
579   if (length < sizeof(struct ip)) {
580     return;
581   }
582 
583   // snap off the ip portion
584   ip = (struct ip*)(packet + sizeof(struct ether_header));
585 
586   // get utility params for sanity checking
587   len     = ntohs(ip->ip_len);
588   hlen    = ip->ip_hl;
589   version = ip->ip_v;
590 
591   // let's not do ipv6 just yet
592   if(version != 4) {
593     return;
594   }
595 
596   // make sure we have a sane header length
597   if(hlen < 5 ) {
598     return;
599   }
600 
601   // do we have the everything we are supposed to?
602   if(length < len) {
603     return;
604   }
605 
606   // make sure we are only processing the first fragment
607   off = ntohs(ip->ip_off);
608   if((off & 0x1fff) == 0 ) {
609 
610     // get the source ip
611     ip_src.s_addr = ip->ip_src.s_addr;
612 
613     // process udp packets
614     if ( ip->ip_p == 17 ) {
615       udp = (struct udphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
616 
617       // try to make sure it is safe to cast packet into dns structure
618       if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+sizeof(struct udphdr)) >= caplen ) {
619         return;
620       }
621       else {
622         // populate dns header
623         dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) );
624         data = (unsigned char *) packet +sizeof(struct ether_header) + sizeof (struct ip) + sizeof (struct udphdr) + sizeof(struct my_dns);
625       }
626     }
627 
628     // process tcp packets
629     else if ( ip->ip_p == 6 ) {
630       tcp = (struct tcphdr *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) );
631 
632       // ignore packets without push flag set
633       if (! (tcp->th_flags & TH_PUSH)) return;
634 
635       // try to make sure it is safe to cast packet into dns structure
636       if ( (sizeof(struct my_dns)+sizeof(struct ether_header)+sizeof(struct ip)+(tcp->th_off * sizeof(u_int32_t)) + sizeof(u_int16_t)) >= caplen ) {
637         return;
638       }
639       else {
640         // populate dns header
641         // tcp dns lookups also include a 16bit length field = dns header + data.
642         dns = (struct my_dns *) ( (char *) packet + sizeof(struct ether_header)+ sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t) + sizeof(u_int16_t)));
643         data = (unsigned char *) packet + sizeof(struct ether_header) + sizeof (struct ip) + (tcp->th_off * sizeof(u_int32_t)) + sizeof(struct my_dns) + sizeof(u_int16_t);
644       }
645     }
646 
647     // hmm.. not tcp, not udp.. move on.
648     else {
649       return;
650     }
651 
652     // we only want queries, not responses
653     if (  dns->dns_flags1 & 0x80 ) {
654       return;
655     }
656 
657     // ignore packets with no questions
658     if (ntohs(dns->dns_qdcount) == 0) {
659       return;
660     }
661 
662     // get the domain name and query type
663     tlen=dpos=0;
664     for (;(*data)&&((void *)data<((void *)packet+caplen-1)); data++) {
665       if (!tlen) tlen=*data;
666       for (;(tlen&&((void *)data<((void *)packet+caplen-1)));tlen--){
667         data++;
668         // bail on an invalid dns char
669         if(!valid_dns_char(*data)) {
670           return;
671         }
672         if (dpos<NS_MAXDNAME) dname[dpos++] = *data;
673       }
674       if (dpos<NS_MAXDNAME) dname[dpos++] = '.';
675     }
676     dname[dpos]='\0';
677 
678     // be careful not to walk past the end of the captured data
679     if ( (void *)data < ((void *)packet+caplen-3) ) {
680       data+=2;
681       qtype = *data;
682     }
683     else {
684       return;
685     }
686 
687     if( option_D ) {
688       printf("src: %-15s  proto: %s  qtype: 0x%02x  domain: %s\n", (inet_ntoa(ip_src)),
689       (ip->ip_p == 17 ? "udp" : "tcp"), qtype, dname);
690     }
691 
692     // add packet to bucket array
693     if (ntohs(dns->dns_qdcount)&&qtype) {
694       add_to_bucket( &ip_src, ip->ip_p, 1, qtype );
695     }
696   }
697   return;
698 }
699 
700 // main logic
701 // some pcap code borrowed from http://www.cet.nau.edu/~mc8/Socket/Tutorials/section1.html
main(int argc,char ** argv)702   int main(int argc,char **argv){
703   char *dev = NULL;
704   pthread_t thread;
705   char errbuf[PCAP_ERRBUF_SIZE];
706   pcap_t* descr;
707   struct bpf_program fp;      /* hold compiled program     */
708   bpf_u_int32 maskp=0;          /* subnet mask               */
709   bpf_u_int32 netp=0;           /* ip                        */
710   char *filter = NULL;
711   char *dst_addr = NULL;
712   char *dst_mask = NULL;
713   struct sigaction sa;
714   struct in_addr addr,tmpaddr;
715   u_int f_size;
716   char *name = NULL;
717   u_int c = 0;
718 
719   if ( ( name = (char *)strdup(argv[0]) ) == NULL) malloc_fail("name", strlen(argv[0]) );
720 
721   // loop through command line options and get options
722   while(1) {
723     c = getopt(argc, argv,"i:t:a:w:x:m:A:M:QbdDvsh");
724 
725     if (c==-1) break;
726     switch(c) {
727       case 0:
728         break;
729       case 'i':
730         if (optarg) {
731           if ( ( dev = (char *)strdup(optarg) ) == NULL) malloc_fail("dev", strlen(optarg) );
732         }
733         break;
734       case 't':
735         if (optarg) {
736           if ( abs (atoi(optarg)) > 0) {
737             option_t = abs( atoi(optarg));
738           }
739         }
740         break;
741       case 'a':
742         if (optarg) {
743           if ( abs (atoi(optarg)) > 10) {
744             option_a = abs( atoi(optarg));
745           }
746         }
747         break;
748       case 'w':
749         if (optarg) {
750           if ( abs (atoi(optarg)) > 1) {
751             option_w = abs( atoi(optarg));
752           }
753         }
754         break;
755       case 'x':
756         if (optarg) {
757           if ( abs (atoi(optarg)) > 10) {
758             option_x = abs( atoi(optarg));
759           }
760         }
761         break;
762       case 'm':
763         if (optarg) {
764           if ( abs (atoi(optarg)) > 0) {
765             option_m = abs( atoi(optarg));
766           }
767         }
768         break;
769       case 'M':
770         if (optarg && (dst_mask == NULL) ) {
771           if ( inet_aton(optarg, &tmpaddr) ) {
772             if ( ( dst_mask = (char *)strdup(optarg) ) == NULL) malloc_fail("filter mask", strlen(optarg) );
773             option_M=1;
774           } else {
775             fprintf(stderr,"Invalid filter mask \"%s\"\n",optarg);
776             option_h = 1;
777           }
778         }
779         break;
780       case 'A':
781         if (optarg && (dst_addr == NULL) ) {
782           if ( inet_aton(optarg, &tmpaddr) ) {
783             if ( ( dst_addr = (char *)strdup(optarg) ) == NULL) malloc_fail("dest filter", strlen(optarg) );
784             option_A=1;
785           } else {
786             fprintf(stderr,"Invalid filter address \"%s\"\n",optarg);
787             option_h = 1;
788           }
789         }
790         break;
791       case 'Q':
792         option_Q = 1;
793         break;
794       case 'b':
795         option_b = 1;
796         break;
797       case 'd':
798         option_d = 1;
799         break;
800       case 'D':
801         option_D = 1;
802          break;
803       case 'v':
804         option_v++;
805         break;
806       case 's':
807         option_s = 1;
808         break;
809       case 'h':
810         option_h = 1;
811         break;
812       case 'z':
813         target_ip = optarg;
814         break;
815       case 'p':
816         target_port = atoi(optarg);
817         break;
818       default:
819         break;
820     }
821   }
822 
823   // display usage info if needed
824   if (optind<argc) option_h = 1;
825 
826   if (option_h) {
827     fprintf(stderr,"dns_flood_detector, version %s\n",VERSION);
828     fprintf(stderr,"Usage: %s [OPTION]\n\n",name);
829     fprintf(stderr,"-i IFNAME                 specify device name to listen on\n");
830     fprintf(stderr,"-t N                      alarm at >N queries per second\n");
831     fprintf(stderr,"-a N                      reset alarm after N seconds\n");
832     fprintf(stderr,"-w N                      calculate stats every N seconds\n");
833     fprintf(stderr,"-x N                      create N buckets\n");
834     fprintf(stderr,"-m N                      report overall stats every N seconds\n");
835     fprintf(stderr,"-A addr                   filter for specific address\n");
836     fprintf(stderr,"-M mask                   netmask for filter (in conjunction with -A)\n");
837     fprintf(stderr,"-Q                        don't filter by local interface address\n");
838     fprintf(stderr,"-b                        run in foreground in bindsnap mode\n");
839     fprintf(stderr,"-d                        run in background in daemon mode\n");
840     fprintf(stderr,"-D                        dump dns packets (implies -b)\n");
841     fprintf(stderr,"-v                        verbose output - use again for more verbosity\n");
842     fprintf(stderr,"-s                        send source-IP data to network collector as JSON\n");
843     fprintf(stderr,"-z addr                   address to send stats to (default 226.1.1.2)\n");
844     fprintf(stderr,"-p N                      UDP port to send stats to (default 2000)\n");
845     fprintf(stderr,"-h                        display this usage information\n");
846     exit(1);
847   }
848 
849   if ( target_ip == NULL ) {
850     target_ip = DEFAULT_IP;
851   }
852 
853   // if dumping packets, force option_b and disable option_d
854   if( option_D ) {
855     if( ! option_b )
856       option_b = 1;
857 
858     if( option_d )
859       option_d = 0;
860   }
861 
862   if ( ( option_Q ) && ( option_A ) ) {
863     fprintf(stderr,"%s couldn't start\n",name);
864     fprintf(stderr,"You can't specify both -A (address filter) and -Q (no filter)\n");
865     exit(1);
866   }
867 
868   if ( ( ! option_d ) && ( ! option_b ) ) {
869     fprintf(stderr,"%s couldn't start\n",name);
870     fprintf(stderr,"You must specify either -d (daemon) or -b (bindsnap)\n");
871     exit(1);
872   }
873   free(name);
874 
875   // set up for daemonized operation unless running in bindsnap mode
876   if ( ! option_b ) {
877     openlog("dns_flood_detector",LOG_PID|LOG_CONS,LOG_DAEMON);
878     syslog(LOG_NOTICE,"dns_flood_detector starting");
879 
880     // daemonize unless running in bindsnap mode
881     daemonize();
882 
883     // set up signal handlers
884     sa.sa_handler=exit;
885     sa.sa_flags=0;
886     if(sigaction(SIGTERM,&sa,NULL)) {
887       syslog(LOG_ERR,"Unable to set signal handler: %s.  Exiting.",strerror(errno));
888     }
889   }
890 
891   // find a valid device to open
892   if(dev == NULL && ( (dev=pcap_lookupdev(errbuf)) == NULL ) ){
893     fprintf(stderr,"unable to bind to valid device\n");
894     exit(1);
895   }
896 
897   /* restrict to queries to primary local address? */
898   if (option_Q) {
899     f_size = strlen("port 53 ");
900     if ( ( filter = (char *) malloc ( f_size+1) ) == NULL ) malloc_fail( "filter", f_size+1 );
901     snprintf( filter, f_size, "port 53");
902   } else {
903     if (! option_A) {
904       // get network address and netmask for device
905       pcap_lookupnet(dev,&netp,&maskp,errbuf);
906 
907       // set up filter with local network
908       addr.s_addr = (unsigned long int)netp;
909       if ( ( dst_addr = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_addr", strlen((char *)inet_ntoa(addr))+1 );
910       strncpy(dst_addr,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
911       dst_addr[strlen((char *)inet_ntoa(addr))]='\0';
912 
913       addr.s_addr = (unsigned long int)maskp;
914 
915       if (!option_M) {
916         if ( ( dst_mask = (char *)malloc( strlen((char *)inet_ntoa(addr))+1) ) == NULL ) malloc_fail("dest_mask", strlen((char *)inet_ntoa(addr))+1 );
917         strncpy(dst_mask,(char*)inet_ntoa(addr),strlen((char *)inet_ntoa(addr)));
918         dst_mask[strlen((char *)inet_ntoa(addr))]='\0';
919       }
920     } else {
921       // we're using an address from -A
922       if (!option_M) {
923         // if no mask was specified, then use just a host mask
924         if ( ( dst_mask = (char *)malloc(16) ) == NULL ) malloc_fail("dest_mask", 16);
925         strncpy(dst_mask,"255.255.255.255",15);
926       }
927     }
928 
929     f_size = strlen("port 53 and dst net mask   ")+ strlen(dst_mask)+ strlen(dst_addr);
930     if ( ( filter = (char *) malloc ( f_size+1) ) == NULL ) malloc_fail( "filter", f_size+1 );
931     snprintf( filter, f_size, "port 53 and dst net %s mask %s", dst_addr, dst_mask);
932 
933     free (dst_mask);
934     free (dst_addr);
935   }
936 
937   if ( option_b && option_v ) {
938     printf("using filter \"%s\" on dev %s\n", filter, dev);
939   }
940 
941   // open device for reading only local traffic
942   descr = pcap_open_live(dev,1500,0,1,errbuf);
943   if(descr == NULL) {
944     fprintf(stderr,"unable to open device %s\n",dev);
945     exit(1);
946   }
947 
948   // compile filter
949   if(pcap_compile(descr,&fp,filter,0,netp) == -1) {
950     fprintf(stderr,"error compiling filter: %s\n",pcap_geterr(descr));
951     exit(1);
952   }
953 
954   // set filter
955   if(pcap_setfilter(descr,&fp) == -1){
956     fprintf(stderr,"error setting filter: %s\n",pcap_geterr(descr));
957     exit(1);
958   }
959 
960   // initialize buckets and mark overall stats bucket
961   init_buckets();
962   totals = option_x;
963 
964   // create mutex lock
965   if (pthread_mutex_init(&stats_lock, NULL) < 0) {
966     exit(1);
967   }
968 
969   // launch watcher thread
970   if (pthread_create (&thread, NULL, run_stats, (void *)0)) {
971     exit(1);
972   }
973 
974   // main pcap loop
975   pcap_loop(descr,-1,&handle_IP,NULL);
976 
977   // done
978   closelog();
979   return 0;
980 }
981 
982 // daemonize the process
daemonize(void)983 int daemonize(void) {
984   pid_t pid;
985   int fd;
986   int a;
987 
988   fd=open("/dev/null",O_RDWR);
989   if(fd<0) {
990     syslog(LOG_ERR,"Failed to open /dev/null: %s.  Exiting.",strerror(errno));
991     exit(1);
992   }
993 
994   dup2(fd,0);
995   dup2(fd,1);
996   dup2(fd,2);
997 
998   if((pid=fork())<0) {
999     syslog(LOG_ERR,"Fork failed: %s.  Exiting.",strerror(errno));
1000     exit(1);
1001   }
1002   else if (pid!=0) {
1003     exit(0);
1004   }
1005 
1006   setsid();
1007   a = chdir("/");
1008   umask(0);
1009   return 0;
1010 }
1011 
malloc_fail(char * var,int size)1012 int malloc_fail( char * var, int size ) {
1013   // print error to stderr if running in bindsnap mode
1014   if (option_b) {
1015     fprintf(stderr, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
1016   }
1017   else {
1018     syslog(LOG_ERR, "our OS wouldn't let me malloc %d bytes for a new %s. giving up", size, var);
1019   }
1020   exit(1);
1021 }
1022 
microsleep(unsigned int usec)1023 int microsleep(unsigned int usec) {
1024   struct timeval        timeout;
1025   timeout.tv_sec = usec / 1000000;
1026   timeout.tv_usec = usec % 1000000;
1027 
1028   while ((select(0, (fd_set *) 0, (fd_set *) 0,(fd_set *) 0, &timeout) < 0) && (errno == EINTR));
1029   return(0);
1030 }
1031 
1032