named plugin: Many many more fixes to get the plugin working.
[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 #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
165
166 /*
167  * Global variables
168  */
169 pcap_t *pcap = NULL;
170 static struct in_addr ignore_addr;
171 static int sld_flag = 0;
172 static int nld_flag = 0;
173
174 static int query_count_intvl = 0;
175 static int query_count_total = 0;
176 int qtype_counts[T_MAX];
177 static int opcode_counts[OP_MAX];
178 static int qclass_counts[C_MAX];
179 static AgentAddr *Sources = NULL;
180 static AgentAddr *Destinations = NULL;
181 static StringCounter *Tlds = NULL;
182 static StringCounter *Slds = NULL;
183 static StringCounter *Nlds = NULL;
184 static StringAddrCounter *SSC2 = NULL;
185 static StringAddrCounter *SSC3 = NULL;
186 #ifdef __OpenBSD__
187 static struct bpf_timeval last_ts;
188 #else
189 static struct timeval last_ts;
190 #endif
191
192 static AgentAddr *
193 AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
194 {
195     AgentAddr **T;
196     for (T = headP; (*T); T = &(*T)->next)
197         if ((*T)->src.s_addr == a.s_addr)
198             return (*T);
199     (*T) = calloc(1, sizeof(**T));
200     (*T)->src = a;
201     return (*T);
202 }
203
204 static StringCounter *
205 StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
206 {
207     StringCounter **T;
208     for (T = headP; (*T); T = &(*T)->next)
209         if (0 == strcmp((*T)->s, s))
210             return (*T);
211     (*T) = calloc(1, sizeof(**T));
212     (*T)->s = strdup(s);
213     return (*T);
214 }
215
216 static StringAddrCounter *
217 StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
218 {
219     StringAddrCounter **T;
220     for (T = headP; (*T); T = &(*T)->next)
221         if (0 == strcmp((*T)->str, str))
222             if ((*T)->src.s_addr == a.s_addr)
223                 return (*T);
224     (*T) = calloc(1, sizeof(**T));
225     (*T)->str = strdup(str);
226     (*T)->src = a;
227     return (*T);
228 }
229
230 #define RFC1035_MAXLABELSZ 63
231 static int
232 rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
233 )
234 {
235     off_t no = 0;
236     unsigned char c;
237     size_t len;
238     assert(ns > 0);
239     do {
240         if ((*off) >= sz)
241             break;
242         c = *(buf + (*off));
243         if (c > 191) {
244             /* blasted compression */
245             unsigned short s;
246             off_t ptr;
247             memcpy(&s, buf + (*off), sizeof(s));
248             s = ntohs(s);
249             (*off) += sizeof(s);
250             /* Sanity check */
251             if ((*off) >= sz)
252                 return 1;
253             ptr = s & 0x3FFF;
254             /* Make sure the pointer is inside this message */
255             if (ptr >= sz)
256                 return 2;
257             return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
258         } else if (c > RFC1035_MAXLABELSZ) {
259             /*
260              * "(The 10 and 01 combinations are reserved for future use.)"
261              */
262             break;
263             return 3;
264         } else {
265             (*off)++;
266             len = (size_t) c;
267             if (len == 0)
268                 break;
269             if (len > (ns - 1))
270                 len = ns - 1;
271             if ((*off) + len > sz)      /* message is too short */
272                 return 4;
273             memcpy(name + no, buf + (*off), len);
274             (*off) += len;
275             no += len;
276             *(name + (no++)) = '.';
277         }
278     } while (c > 0);
279     *(name + no - 1) = '\0';
280     /* make sure we didn't allow someone to overflow the name buffer */
281     assert(no <= ns);
282     return 0;
283 }
284
285 static const char *
286 QnameToNld(const char *qname, int nld)
287 {
288     const char *t = strrchr(qname, '.');
289     int dotcount = 1;
290     if (NULL == t)
291         t = qname;
292     if (0 == strcmp(t, ".arpa"))
293         dotcount--;
294     while (t > qname && dotcount < nld) {
295         t--;
296         if ('.' == *t)
297             dotcount++;
298     }
299     if (t > qname)
300         t++;
301     return t;
302 }
303
304 static int
305 handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
306 {
307     rfc1035_header qh;
308     unsigned short us;
309     char qname[MAX_QNAME_SZ];
310     unsigned short qtype;
311     unsigned short qclass;
312     off_t offset;
313     char *t;
314     const char *s;
315     int x;
316     StringCounter *sc;
317     StringAddrCounter *ssc;
318
319     fprintf (stderr, "handle_dns (buf = %p, len = %i)\n",
320                     (void *) buf, len);
321
322     if (len < sizeof(qh))
323         return 0;
324
325     memcpy(&us, buf + 00, 2);
326     qh.id = ntohs(us);
327
328     memcpy(&us, buf + 2, 2);
329     us = ntohs(us);
330     qh.qr = (us >> 15) & 0x01;
331     qh.opcode = (us >> 11) & 0x0F;
332     qh.aa = (us >> 10) & 0x01;
333     qh.tc = (us >> 9) & 0x01;
334     qh.rd = (us >> 8) & 0x01;
335     qh.ra = (us >> 7) & 0x01;
336     qh.rcode = us & 0x0F;
337
338     memcpy(&us, buf + 4, 2);
339     qh.qdcount = ntohs(us);
340
341     memcpy(&us, buf + 6, 2);
342     qh.ancount = ntohs(us);
343
344     memcpy(&us, buf + 8, 2);
345     qh.nscount = ntohs(us);
346
347     memcpy(&us, buf + 10, 2);
348     qh.arcount = ntohs(us);
349
350     offset = sizeof(qh);
351     memset(qname, '\0', MAX_QNAME_SZ);
352     x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ);
353     if (0 != x)
354         return 0;
355     if ('\0' == qname[0])
356         strcpy(qname, ".");
357     while ((t = strchr(qname, '\n')))
358         *t = ' ';
359     while ((t = strchr(qname, '\r')))
360         *t = ' ';
361     for (t = qname; *t; t++)
362         *t = tolower(*t);
363
364     memcpy(&us, buf + offset, 2);
365     qtype = ntohs(us);
366     memcpy(&us, buf + offset + 2, 2);
367     qclass = ntohs(us);
368
369     fprintf (stderr, "qtype = %hu\n", qtype);
370
371     /* gather stats */
372     qtype_counts[qtype]++;
373     qclass_counts[qclass]++;
374     opcode_counts[qh.opcode]++;
375
376     s = QnameToNld(qname, 1);
377     sc = StringCounter_lookup_or_add(&Tlds, s);
378     sc->count++;
379
380     if (sld_flag) {
381         s = QnameToNld(qname, 2);
382         sc = StringCounter_lookup_or_add(&Slds, s);
383         sc->count++;
384
385         /* increment StringAddrCounter */
386         ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
387         ssc->count++;
388
389     }
390     if (nld_flag) {
391         s = QnameToNld(qname, 3);
392         sc = StringCounter_lookup_or_add(&Nlds, s);
393         sc->count++;
394
395         /* increment StringAddrCounter */
396         ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
397         ssc->count++;
398
399     }
400     return 1;
401 }
402
403 static int
404 handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
405 {
406     char buf[PCAP_SNAPLEN];
407     fprintf (stderr, "handle_udp (udp = %p, len = %i)\n",
408                     (void *) udp, len);
409     if (ntohs (udp->uh_dport) != 53)
410         return 0;
411     memcpy(buf, udp + 1, len - sizeof(*udp));
412     if (0 == handle_dns(buf, len - sizeof(*udp), sip, dip))
413         return 0;
414     return 1;
415 }
416
417 static int
418 handle_ip(const struct ip *ip, int len)
419 {
420     char buf[PCAP_SNAPLEN];
421     int offset = ip->ip_hl << 2;
422     AgentAddr *clt;
423     AgentAddr *srv;
424     fprintf (stderr, "handle_ip (ip = %p, len = %i)\n",
425                     (void *) ip, len);
426     if (ignore_addr.s_addr)
427         if (ip->ip_src.s_addr == ignore_addr.s_addr)
428             return 0;
429     if (IPPROTO_UDP != ip->ip_p)
430         return 0;
431     memcpy(buf, (void *) ip + offset, len - offset);
432     if (0 == handle_udp((struct udphdr *) buf, len - offset, ip->ip_src, ip->ip_dst))
433         return 0;
434     clt = AgentAddr_lookup_or_add(&Sources, ip->ip_src);
435     clt->count++;
436     srv = AgentAddr_lookup_or_add(&Destinations, ip->ip_dst);
437     srv->count++;
438     return 1;
439 }
440
441 #if USE_PPP
442 static int
443 handle_ppp(const u_char * pkt, int len)
444 {
445     char buf[PCAP_SNAPLEN];
446     unsigned short us;
447     unsigned short proto;
448     if (len < 2)
449         return 0;
450     if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
451         pkt += 2;               /* ACFC not used */
452         len -= 2;
453     }
454     if (len < 2)
455         return 0;
456     if (*pkt % 2) {
457         proto = *pkt;           /* PFC is used */
458         pkt++;
459         len--;
460     } else {
461         memcpy(&us, pkt, sizeof(us));
462         proto = ntohs(us);
463         pkt += 2;
464         len -= 2;
465     }
466     if (ETHERTYPE_IP != proto && PPP_IP != proto)
467         return 0;
468     memcpy(buf, pkt, len);
469     return handle_ip((struct ip *) buf, len);
470 }
471
472 #endif
473
474 static int
475 handle_null(const u_char * pkt, int len)
476 {
477     unsigned int family;
478     memcpy(&family, pkt, sizeof(family));
479     if (AF_INET != family)
480         return 0;
481     return handle_ip((struct ip *) (pkt + 4), len - 4);
482 }
483
484 #ifdef DLT_LOOP
485 static int
486 handle_loop(const u_char * pkt, int len)
487 {
488     unsigned int family;
489     memcpy(&family, pkt, sizeof(family));
490     if (AF_INET != ntohl(family))
491         return 0;
492     return handle_ip((struct ip *) (pkt + 4), len - 4);
493 }
494
495 #endif
496
497 #ifdef DLT_RAW
498 static int
499 handle_raw(const u_char * pkt, int len)
500 {
501     return handle_ip((struct ip *) pkt, len);
502 }
503
504 #endif
505
506 static int
507 handle_ether(const u_char * pkt, int len)
508 {
509     char buf[PCAP_SNAPLEN];
510     struct ether_header *e = (void *) pkt;
511     unsigned short etype = ntohs(e->ether_type);
512     fprintf (stderr, "handle_ether (pkt = %p, len = %i)\n",
513                     (void *) pkt, len);
514     if (len < ETHER_HDR_LEN)
515         return 0;
516     pkt += ETHER_HDR_LEN;
517     len -= ETHER_HDR_LEN;
518     if (ETHERTYPE_8021Q == etype) {
519         etype = ntohs(*(unsigned short *) (pkt + 2));
520         pkt += 4;
521         len -= 4;
522     }
523     if (ETHERTYPE_IP != etype)
524         return 0;
525     memcpy(buf, pkt, len);
526     return handle_ip((struct ip *) buf, len);
527 }
528
529 /* public function */
530 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
531 {
532     int status;
533
534     fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
535                     (void *) udata, (void *) hdr, (void *) pkt,
536                     hdr->caplen);
537
538     if (hdr->caplen < ETHER_HDR_LEN)
539         return;
540
541     switch (pcap_datalink (pcap))
542     {
543         case DLT_EN10MB:
544             status = handle_ether (pkt, hdr->caplen);
545             break;
546 #if USE_PPP
547         case DLT_PPP:
548             status = handle_ppp (pkt, hdr->caplen);
549             break;
550 #endif
551 #ifdef DLT_LOOP
552         case DLT_LOOP:
553             status = handle_loop (pkt, hdr->caplen);
554             break;
555 #endif
556 #ifdef DLT_RAW
557         case DLT_RAW:
558             status = handle_raw (pkt, hdr->caplen);
559             break;
560 #endif
561         case DLT_NULL:
562             status = handle_null (pkt, hdr->caplen);
563             break;
564
565         default:
566             fprintf (stderr, "unsupported data link type %d\n",
567                     pcap_datalink(pcap));
568             status = 0;
569             break;
570     } /* switch (pcap_datalink(pcap)) */
571
572     if (0 == status)
573         return;
574
575     query_count_intvl++;
576     query_count_total++;
577     last_ts = hdr->ts;
578 }
579
580 char *
581 qtype_str(int t)
582 {
583     static char buf[30];
584     switch (t) {
585     case T_A:
586         return "A";
587         break;
588     case T_NS:
589         return "NS";
590         break;
591     case T_CNAME:
592         return "CNAME";
593         break;
594     case T_SOA:
595         return "SOA";
596         break;
597     case T_PTR:
598         return "PTR";
599         break;
600     case T_MX:
601         return "MX";
602         break;
603     case T_TXT:
604         return "TXT";
605         break;
606     case T_SIG:
607         return "SIG";
608         break;
609     case T_KEY:
610         return "KEY";
611         break;
612     case T_AAAA:
613         return "AAAA";
614         break;
615     case T_LOC:
616         return "LOC";
617         break;
618     case T_SRV:
619         return "SRV";
620         break;
621     case T_A6:
622         return "A6";
623         break;
624     case T_ANY:
625         return "ANY";
626         break;
627     default:
628         snprintf(buf, 30, "#%d", t);
629         return buf;
630     }
631     /* NOTREACHED */
632 }
633
634 char *
635 opcode_str(int o)
636 {
637     static char buf[30];
638     switch (o) {
639     case 0:
640         return "Query";
641         break;
642     case 1:
643         return "Iquery";
644         break;
645     case 2:
646         return "Status";
647         break;
648     case 4:
649         return "Notify";
650         break;
651     case 5:
652         return "Update";
653         break;
654     default:
655         snprintf(buf, 30, "Opcode%d", o);
656         return buf;
657     }
658     /* NOTREACHED */
659 }
660
661 #if 0
662 static int
663 main(int argc, char *argv[])
664 {
665     char errbuf[PCAP_ERRBUF_SIZE];
666     int x;
667     struct stat sb;
668     int readfile_state = 0;
669     struct bpf_program fp;
670
671     port53 = htons(53);
672     SubReport = Sources_report;
673     ignore_addr.s_addr = 0;
674     progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
675     srandom(time(NULL));
676     ResetCounters();
677
678     while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
679         switch (x) {
680         case 'a':
681             anon_flag = 1;
682             break;
683         case 's':
684             sld_flag = 1;
685             break;
686         case 't':
687             nld_flag = 1;
688             break;
689         case 'p':
690             promisc_flag = 0;
691             break;
692         case 'b':
693             bpf_program_str = strdup(optarg);
694             break;
695         case 'i':
696             ignore_addr.s_addr = inet_addr(optarg);
697             break;
698         case 'f':
699             set_filter(optarg);
700             break;
701         default:
702             usage();
703             break;
704         }
705     }
706     argc -= optind;
707     argv += optind;
708
709     if (argc < 1)
710         usage();
711     device = strdup(argv[0]);
712
713     if (0 == stat(device, &sb))
714         readfile_state = 1;
715     if (readfile_state) {
716         pcap = pcap_open_offline(device, errbuf);
717     } else {
718         pcap = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
719     }
720     if (NULL == pcap) {
721         fprintf(stderr, "pcap_open_*: %s\n", errbuf);
722         exit(1);
723     }
724
725     if (0 == isatty(1)) {
726         if (0 == readfile_state) {
727             fprintf(stderr, "Non-interactive mode requires savefile argument\n");
728             exit(1);
729         }
730         interactive = 0;
731         print_func = printf;
732     }
733
734     memset(&fp, '\0', sizeof(fp));
735     x = pcap_compile(pcap, &fp, bpf_program_str, 1, 0);
736     if (x < 0) {
737         fprintf(stderr, "pcap_compile failed\n");
738         exit(1);
739     }
740     x = pcap_setfilter(pcap, &fp);
741     if (x < 0) {
742         fprintf(stderr, "pcap_setfilter failed\n");
743         exit(1);
744     }
745
746     /*
747      * non-blocking call added for Mac OS X bugfix.  Sent by Max Horn.
748      * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
749      */
750     x = pcap_setnonblock(pcap, 1, errbuf);
751     if (x < 0) {
752         fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
753         exit(1);
754     }
755
756     switch (pcap_datalink(pcap)) {
757     case DLT_EN10MB:
758         handle_datalink = handle_ether;
759         break;
760 #if USE_PPP
761     case DLT_PPP:
762         handle_datalink = handle_ppp;
763         break;
764 #endif
765 #ifdef DLT_LOOP
766     case DLT_LOOP:
767         handle_datalink = handle_loop;
768         break;
769 #endif
770 #ifdef DLT_RAW
771     case DLT_RAW:
772         handle_datalink = handle_raw;
773         break;
774 #endif
775     case DLT_NULL:
776         handle_datalink = handle_null;
777         break;
778     default:
779         fprintf(stderr, "unsupported data link type %d\n",
780             pcap_datalink(pcap));
781         return 1;
782         break;
783     }
784     if (interactive) {
785         init_curses();
786         while (0 == Quit) {
787             if (readfile_state < 2) {
788                 /*
789                  * On some OSes select() might return 0 even when
790                  * there are packets to process.  Thus, we always
791                  * ignore its return value and just call pcap_dispatch()
792                  * anyway.
793                  */
794                 if (0 == readfile_state)        /* interactive */
795                     pcap_select(pcap, 1, 0);
796                 x = pcap_dispatch(pcap, 50, handle_pcap, NULL);
797             }
798             if (0 == x && 1 == readfile_state) {
799                 /* block on keyboard until user quits */
800                 readfile_state++;
801                 nodelay(w, 0);
802             }
803             keyboard();
804             cron_pre();
805             report();
806             cron_post();
807         }
808         endwin();               /* klin, Thu Nov 28 08:56:51 2002 */
809     } else {
810         while (pcap_dispatch(pcap, 50, handle_pcap, NULL))
811                 (void) 0;
812         cron_pre();
813         Sources_report(); print_func("\n");
814         Destinatioreport(); print_func("\n");
815         Qtypes_report(); print_func("\n");
816         Opcodes_report(); print_func("\n");
817         Tld_report(); print_func("\n");
818         Sld_report(); print_func("\n");
819         Nld_report(); print_func("\n");
820         SldBySource_report();
821     }
822
823     pcap_close(pcap);
824     return 0;
825 } /* static int main(int argc, char *argv[]) */
826 #endif