Added src/utils_avltree.[ch]: An implementation of an AVL-tree.
[collectd.git] / src / dns.c
1 /**
2  * collectd - src/dns.c
3  * Copyright (C) 2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_debug.h"
28
29 #if HAVE_SYS_POLL_H
30 # include <sys/poll.h>
31 #endif
32
33 #define MODULE_NAME "dns"
34
35 #if HAVE_LIBPCAP
36 # define NAMED_HAVE_CONFIG 1
37 #else
38 # define NAMED_HAVE_CONFIG 0
39 #endif
40
41 #if HAVE_LIBPCAP
42 # include "utils_dns.h"
43 # define NAMED_HAVE_READ 1
44 #else
45 # define NAMED_HAVE_READ 0
46 #endif
47
48 struct counter_list_s
49 {
50         unsigned int key;
51         unsigned int value;
52         struct counter_list_s *next;
53 };
54 typedef struct counter_list_s counter_list_t;
55
56 static char *traffic_file   = "dns/dns_traffic.rrd";
57 static char *qtype_file   = "dns/qtype-%s.rrd";
58 static char *opcode_file  = "dns/opcode-%s.rrd";
59 static char *rcode_file   = "dns/rcode-%s.rrd";
60
61 static char *traffic_ds_def[] =
62 {
63         /* Limit to 1GBit/s */
64         "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
65         "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
66         NULL
67 };
68 static int traffic_ds_num = 2;
69
70 static char *qtype_ds_def[] =
71 {
72         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
73         NULL
74 };
75 static int qtype_ds_num = 1;
76
77 static char *opcode_ds_def[] =
78 {
79         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
80         NULL
81 };
82 static int opcode_ds_num = 1;
83
84 static char *rcode_ds_def[] =
85 {
86         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
87         NULL
88 };
89 static int rcode_ds_num = 1;
90
91 #if NAMED_HAVE_CONFIG
92 #if HAVE_LIBPCAP
93 static char *config_keys[] =
94 {
95         "Interface",
96         "IgnoreSource",
97         NULL
98 };
99 static int config_keys_num = 2;
100 #endif /* HAVE_LIBPCAP */
101 #endif /* NAMED_HAVE_CONFIG */
102
103 #if HAVE_LIBPCAP
104 #define PCAP_SNAPLEN 1460
105 static char   *pcap_device = NULL;
106 static int     pipe_fd = -1;
107
108 static unsigned int    tr_queries;
109 static unsigned int    tr_responses;
110 static counter_list_t *qtype_list;
111 static counter_list_t *opcode_list;
112 static counter_list_t *rcode_list;
113 #endif
114
115 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
116 {
117         counter_list_t *entry;
118
119         DBG ("counter_list_search (list = %p, key = %u)",
120                         (void *) *list, key);
121
122         for (entry = *list; entry != NULL; entry = entry->next)
123                 if (entry->key == key)
124                         break;
125
126         DBG ("return (%p)", (void *) entry);
127         return (entry);
128 }
129
130 static counter_list_t *counter_list_create (counter_list_t **list,
131                 unsigned int key, unsigned int value)
132 {
133         counter_list_t *entry;
134
135         DBG ("counter_list_create (list = %p, key = %u, value = %u)",
136                         (void *) *list, key, value);
137
138         entry = (counter_list_t *) malloc (sizeof (counter_list_t));
139         if (entry == NULL)
140                 return (NULL);
141
142         memset (entry, 0, sizeof (counter_list_t));
143         entry->key = key;
144         entry->value = value;
145
146         if (*list == NULL)
147         {
148                 *list = entry;
149         }
150         else
151         {
152                 counter_list_t *last;
153
154                 last = *list;
155                 while (last->next != NULL)
156                         last = last->next;
157
158                 last->next = entry;
159         }
160
161         DBG ("return (%p)", (void *) entry);
162         return (entry);
163 }
164
165 static void counter_list_add (counter_list_t **list,
166                 unsigned int key, unsigned int increment)
167 {
168         counter_list_t *entry;
169
170         DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
171                         (void *) *list, key, increment);
172
173         entry = counter_list_search (list, key);
174
175         if (entry != NULL)
176         {
177                 entry->value += increment;
178         }
179         else
180         {
181                 counter_list_create (list, key, increment);
182         }
183         DBG ("return ()");
184 }
185
186 static int counter_list_send (counter_list_t *list, int fd)
187 {
188         counter_list_t *cl;
189         unsigned int values[2 * T_MAX];
190         unsigned int values_num;
191
192         if (fd < 0)
193                 return (-1);
194
195         values_num = 0;
196
197         for (cl = list;
198                         (cl != NULL) && (values_num < T_MAX);
199                         cl = cl->next)
200         {
201                 values[2 * values_num] = cl->key;
202                 values[(2 * values_num) + 1] = cl->value;
203                 values_num++;
204         }
205
206         DBG ("swrite (fd = %i, values_num = %i)", fd, values_num);
207         if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0)
208         {
209                 DBG ("Writing to fd failed: %s", strerror (errno));
210                 syslog (LOG_ERR, "dns plugin: Writing to fd failed: %s",
211                                 strerror (errno));
212                 return (-1);
213         }
214
215         if (values_num == 0)
216                 return (0);
217
218         DBG ("swrite (fd = %i, values = %p, size = %i)",
219                         fd, (void *) values, (int) (sizeof (int) * values_num));
220         if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
221         {
222                 DBG ("Writing to pipe failed: %s", strerror (errno));
223                 syslog (LOG_ERR, "dns plugin: Writing to pipe failed: %s",
224                                 strerror (errno));
225                 return (-1);
226         }
227
228         return (values_num);
229 }
230 #if NAMED_HAVE_CONFIG
231 static int dns_config (char *key, char *value)
232 {
233 #if HAVE_LIBPCAP
234         if (strcasecmp (key, "Interface") == 0)
235         {
236                 if (pcap_device != NULL)
237                         free (pcap_device);
238                 if ((pcap_device = strdup (value)) == NULL)
239                         return (1);
240         }
241         else if (strcasecmp (key, "IgnoreSource") == 0)
242         {
243                 if (value != NULL)
244                         ignore_list_add_name (value);
245         }
246         else
247         {
248                 return (-1);
249         }
250
251         return (0);
252 #endif /* HAVE_LIBPCAP */
253 }
254 #endif /* NAMED_HAVE_CONFIG */
255
256 static void dns_child_callback (const rfc1035_header_t *dns)
257 {
258         if (dns->qr == 0)
259         {
260                 /* This is a query */
261                 tr_queries += dns->length;
262                 counter_list_add (&qtype_list,  dns->qtype,  1);
263         }
264         else
265         {
266                 /* This is a reply */
267                 tr_responses += dns->length;
268                 counter_list_add (&rcode_list,  dns->rcode,  1);
269         }
270
271         /* FIXME: Are queries, replies or both interesting? */
272         counter_list_add (&opcode_list, dns->opcode, 1);
273 }
274
275 static void dns_child_loop (void)
276 {
277         pcap_t *pcap_obj;
278         char    pcap_error[PCAP_ERRBUF_SIZE];
279         struct  bpf_program fp;
280
281         struct pollfd poll_fds[2];
282         int status;
283
284         /* Don't catch these signals */
285         signal (SIGINT, SIG_DFL);
286         signal (SIGTERM, SIG_DFL);
287
288         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
289         DBG ("Creating PCAP object..");
290         pcap_obj = pcap_open_live (pcap_device,
291                         PCAP_SNAPLEN,
292                         0 /* Not promiscuous */,
293                         0 /* no read timeout */,
294                         pcap_error);
295         if (pcap_obj == NULL)
296         {
297                 syslog (LOG_ERR, "dns plugin: Opening interface `%s' failed: %s",
298                                 (pcap_device != NULL) ? pcap_device : "any",
299                                 pcap_error);
300                 close (pipe_fd);
301                 pipe_fd = -1;
302                 return;
303         }
304
305         memset (&fp, 0, sizeof (fp));
306         if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
307         {
308                 DBG ("pcap_compile failed");
309                 syslog (LOG_ERR, "dns plugin: pcap_compile failed");
310                 close (pipe_fd);
311                 pipe_fd = -1;
312                 return;
313         }
314         if (pcap_setfilter (pcap_obj, &fp) < 0)
315         {
316                 DBG ("pcap_setfilter failed");
317                 syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
318                 close (pipe_fd);
319                 pipe_fd = -1;
320                 return;
321         }
322
323         DBG ("PCAP object created.");
324
325         dnstop_set_pcap_obj (pcap_obj);
326         dnstop_set_callback (dns_child_callback);
327
328         /* Set up pipe end */
329         poll_fds[0].fd = pipe_fd;
330         poll_fds[0].events = POLLOUT;
331
332         /* Set up pcap device */
333         poll_fds[1].fd = pcap_fileno (pcap_obj);
334         poll_fds[1].events = POLLIN | POLLPRI;
335
336         while (pipe_fd > 0)
337         {
338                 DBG ("poll (...)");
339                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
340
341                 /* Signals are not caught, but this is very handy when
342                  * attaching to the process with a debugger. -octo */
343                 if ((status < 0) && (errno == EINTR))
344                 {
345                         errno = 0;
346                         continue;
347                 }
348
349                 if (status < 0)
350                 {
351                         syslog (LOG_ERR, "dns plugin: poll(2) failed: %s",
352                                         strerror (errno));
353                         break;
354                 }
355
356                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
357                 {
358                         DBG ("Pipe closed. Exiting.");
359                         syslog (LOG_NOTICE, "dns plugin: Pipe closed. Exiting.");
360                         break;
361                 }
362                 else if (poll_fds[0].revents & POLLOUT)
363                 {
364                         DBG ("Sending data..");
365
366                         DBG ("swrite (pipe_fd = %i, tr_queries = %i)", pipe_fd, tr_queries);
367                         if (swrite (pipe_fd, (const void *) &tr_queries, sizeof (tr_queries)) != 0)
368                         {
369                                 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
370                                 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
371                                                 strerror (errno));
372                                 return;
373                         }
374
375                         DBG ("swrite (pipe_fd = %i, tr_responses = %i)", pipe_fd, tr_responses);
376                         if (swrite (pipe_fd, (const void *) &tr_responses, sizeof (tr_responses)) != 0)
377                         {
378                                 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
379                                 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
380                                                 strerror (errno));
381                                 return;
382                         }
383
384                         counter_list_send (qtype_list, pipe_fd);
385                         counter_list_send (opcode_list, pipe_fd);
386                         counter_list_send (rcode_list, pipe_fd);
387                 }
388
389                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
390                 {
391                         DBG ("pcap-device closed. Exiting.");
392                         syslog (LOG_ERR, "dns plugin: pcap-device closed. Exiting.");
393                         break;
394                 }
395                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
396                 {
397                         status = pcap_dispatch (pcap_obj,
398                                         10 /* Only handle 10 packets at a time */,
399                                         handle_pcap /* callback */,
400                                         NULL /* Whatever this means.. */);
401                         if (status < 0)
402                         {
403                                 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
404                                 syslog (LOG_ERR, "dns plugin: pcap_dispatch failed: %s",
405                                                 pcap_geterr (pcap_obj));
406                                 break;
407                         }
408                 }
409         } /* while (42) */
410
411         DBG ("child is exiting");
412
413         close (pipe_fd);
414         pipe_fd = -1;
415         pcap_close (pcap_obj);
416 } /* static void dns_child_loop (void) */
417
418 static void dns_init (void)
419 {
420 #if HAVE_LIBPCAP
421         int pipe_fds[2];
422         pid_t pid_child;
423
424         tr_queries   = 0;
425         tr_responses = 0;
426
427         if (pipe (pipe_fds) != 0)
428         {
429                 syslog (LOG_ERR, "dns plugin: pipe(2) failed: %s",
430                                 strerror (errno));
431                 return;
432         }
433
434         /* Fork off child */
435         pid_child = fork ();
436         if (pid_child < 0)
437         {
438                 syslog (LOG_ERR, "dns plugin: fork(2) failed: %s",
439                                 strerror (errno));
440                 close (pipe_fds[0]);
441                 close (pipe_fds[1]);
442                 return;
443         }
444         else if (pid_child != 0)
445         {
446                 /* parent: Close the writing end, keep the reading end. */
447                 pipe_fd = pipe_fds[0];
448                 close (pipe_fds[1]);
449         }
450         else
451         {
452                 /* child: Close the reading end, keep the writing end. */
453                 pipe_fd = pipe_fds[1];
454                 close (pipe_fds[0]);
455
456                 dns_child_loop ();
457                 exit (0);
458         }
459
460         /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
461 #endif
462 }
463
464 static void traffic_write (char *host, char *inst, char *val)
465 {
466         rrd_update_file (host, traffic_file, val,
467                         traffic_ds_def, traffic_ds_num);
468 }
469
470 static void qtype_write (char *host, char *inst, char *val)
471 {
472         char file[512];
473         int status;
474
475         status = snprintf (file, 512, qtype_file, inst);
476         if (status < 1)
477                 return;
478         else if (status >= 512)
479                 return;
480
481         rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
482 }
483
484 static void rcode_write (char *host, char *inst, char *val)
485 {
486         char file[512];
487         int status;
488
489         status = snprintf (file, 512, rcode_file, inst);
490         if (status < 1)
491                 return;
492         else if (status >= 512)
493                 return;
494
495         rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
496 }
497
498 static void opcode_write (char *host, char *inst, char *val)
499 {
500         char file[512];
501         int status;
502
503         status = snprintf (file, 512, opcode_file, inst);
504         if (status < 1)
505                 return;
506         else if (status >= 512)
507                 return;
508
509         rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
510 }
511
512 static void traffic_submit (unsigned int queries, unsigned int replies)
513 {
514         char buffer[64];
515         int  status;
516
517         status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
518         if ((status < 1) || (status >= 64))
519                 return;
520
521         plugin_submit ("dns_traffic", "-", buffer);
522 }
523
524 static void qtype_submit (int qtype, unsigned int counter)
525 {
526         char inst[32];
527         char buffer[32];
528         int  status;
529
530         strncpy (inst, qtype_str (qtype), 32);
531         inst[31] = '\0';
532
533         status = snprintf (buffer, 32, "N:%u", counter);
534         if ((status < 1) || (status >= 32))
535                 return;
536
537         plugin_submit ("dns_qtype", inst, buffer);
538 }
539
540 static void rcode_submit (int rcode, unsigned int counter)
541 {
542         char inst[32];
543         char buffer[32];
544         int  status;
545
546         strncpy (inst, rcode_str (rcode), 32);
547         inst[31] = '\0';
548
549         status = snprintf (buffer, 32, "N:%u", counter);
550         if ((status < 1) || (status >= 32))
551                 return;
552
553         plugin_submit ("dns_rcode", inst, buffer);
554 }
555
556 static void opcode_submit (int opcode, unsigned int counter)
557 {
558         char inst[32];
559         char buffer[32];
560         int  status;
561
562         strncpy (inst, opcode_str (opcode), 32);
563         inst[31] = '\0';
564
565         status = snprintf (buffer, 32, "N:%u", counter);
566         if ((status < 1) || (status >= 32))
567                 return;
568
569         plugin_submit ("dns_opcode", inst, buffer);
570 }
571
572 #if NAMED_HAVE_READ
573 static unsigned int dns_read_array (unsigned int *values)
574 {
575         unsigned int values_num;
576
577         if (pipe_fd < 0)
578                 return (0);
579
580         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
581         {
582                 DBG ("Reading from the pipe failed: %s",
583                                 strerror (errno));
584                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
585                                 strerror (errno));
586                 pipe_fd = -1;
587                 return (0);
588         }
589         DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
590
591         assert (values_num <= T_MAX);
592
593         if (values_num == 0)
594                 return (0);
595
596         if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
597         {
598                 DBG ("Reading from the pipe failed: %s",
599                                 strerror (errno));
600                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
601                                 strerror (errno));
602                 pipe_fd = -1;
603                 return (0);
604         }
605
606         return (values_num);
607 }
608
609 static void dns_read (void)
610 {
611         unsigned int values[2 * T_MAX];
612         unsigned int values_num;
613         int i;
614
615         if (pipe_fd < 0)
616                 return;
617
618         if (sread (pipe_fd, (void *) &tr_queries, sizeof (tr_queries)) != 0)
619         {
620                 DBG ("Reading from the pipe failed: %s",
621                                 strerror (errno));
622                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
623                                 strerror (errno));
624                 pipe_fd = -1;
625                 return;
626         }
627         DBG ("sread (pipe_fd = %i, tr_queries = %u)", pipe_fd, tr_queries);
628
629         if (sread (pipe_fd, (void *) &tr_responses, sizeof (tr_responses)) != 0)
630         {
631                 DBG ("Reading from the pipe failed: %s",
632                                 strerror (errno));
633                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
634                                 strerror (errno));
635                 pipe_fd = -1;
636                 return;
637         }
638         DBG ("sread (pipe_fd = %i, tr_responses = %u)", pipe_fd, tr_responses);
639
640         traffic_submit (tr_queries, tr_responses);
641
642         values_num = dns_read_array (values);
643         for (i = 0; i < values_num; i++)
644         {
645                 DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
646                 qtype_submit (values[2 * i], values[(2 * i) + 1]);
647         }
648
649         values_num = dns_read_array (values);
650         for (i = 0; i < values_num; i++)
651         {
652                 DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
653                 opcode_submit (values[2 * i], values[(2 * i) + 1]);
654         }
655
656         values_num = dns_read_array (values);
657         for (i = 0; i < values_num; i++)
658         {
659                 DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
660                 rcode_submit (values[2 * i], values[(2 * i) + 1]);
661         }
662 }
663 #else /* if !NAMED_HAVE_READ */
664 # define dns_read NULL
665 #endif
666
667 void module_register (void)
668 {
669         plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
670         plugin_register ("dns_traffic", NULL, NULL, traffic_write);
671         plugin_register ("dns_qtype", NULL, NULL, qtype_write);
672         plugin_register ("dns_rcode", NULL, NULL, rcode_write);
673         plugin_register ("dns_opcode", NULL, NULL, opcode_write);
674         cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
675 }
676
677 #undef MODULE_NAME