Renamed the `named'-plugin to `dns'-plugin.
[collectd.git] / src / dns.c
1 /**
2  * collectd - src/named.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 "named"
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 *qtype_file   = "named/qtype-%s.rrd";
57 static char *opcode_file  = "named/opcode-%s.rrd";
58 static char *rcode_file   = "named/rcode-%s.rrd";
59
60 static char *qtype_ds_def[] =
61 {
62         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
63         NULL
64 };
65 static int qtype_ds_num = 1;
66
67 static char *opcode_ds_def[] =
68 {
69         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
70         NULL
71 };
72 static int opcode_ds_num = 1;
73
74 static char *rcode_ds_def[] =
75 {
76         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
77         NULL
78 };
79 static int rcode_ds_num = 1;
80
81 #if NAMED_HAVE_CONFIG
82 #if HAVE_LIBPCAP
83 static char *config_keys[] =
84 {
85         "Interface",
86         "IgnoreSource",
87         NULL
88 };
89 static int config_keys_num = 2;
90 #endif /* HAVE_LIBPCAP */
91 #endif /* NAMED_HAVE_CONFIG */
92
93 #if HAVE_LIBPCAP
94 #define PCAP_SNAPLEN 1460
95 static char   *pcap_device = NULL;
96 static int     pipe_fd = -1;
97
98 static counter_list_t *qtype_list;
99 static counter_list_t *opcode_list;
100 static counter_list_t *rcode_list;
101 #endif
102
103 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
104 {
105         counter_list_t *entry;
106
107         DBG ("counter_list_search (list = %p, key = %u)",
108                         (void *) *list, key);
109
110         for (entry = *list; entry != NULL; entry = entry->next)
111                 if (entry->key == key)
112                         break;
113
114         DBG ("return (%p)", (void *) entry);
115         return (entry);
116 }
117
118 static counter_list_t *counter_list_create (counter_list_t **list,
119                 unsigned int key, unsigned int value)
120 {
121         counter_list_t *entry;
122
123         DBG ("counter_list_create (list = %p, key = %u, value = %u)",
124                         (void *) *list, key, value);
125
126         entry = (counter_list_t *) malloc (sizeof (counter_list_t));
127         if (entry == NULL)
128                 return (NULL);
129
130         memset (entry, 0, sizeof (counter_list_t));
131         entry->key = key;
132         entry->value = value;
133
134         if (*list == NULL)
135         {
136                 *list = entry;
137         }
138         else
139         {
140                 counter_list_t *last;
141
142                 last = *list;
143                 while (last->next != NULL)
144                         last = last->next;
145
146                 last->next = entry;
147         }
148
149         DBG ("return (%p)", (void *) entry);
150         return (entry);
151 }
152
153 static void counter_list_add (counter_list_t **list,
154                 unsigned int key, unsigned int increment)
155 {
156         counter_list_t *entry;
157
158         DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
159                         (void *) *list, key, increment);
160
161         entry = counter_list_search (list, key);
162
163         if (entry != NULL)
164         {
165                 entry->value += increment;
166         }
167         else
168         {
169                 counter_list_create (list, key, increment);
170         }
171         DBG ("return ()");
172 }
173
174 static int counter_list_send (counter_list_t *list, int fd)
175 {
176         counter_list_t *cl;
177         unsigned int values[2 * T_MAX];
178         unsigned int values_num;
179
180         if (fd < 0)
181                 return (-1);
182
183         values_num = 0;
184
185         for (cl = list;
186                         (cl != NULL) && (values_num < T_MAX);
187                         cl = cl->next)
188         {
189                 values[2 * values_num] = cl->key;
190                 values[(2 * values_num) + 1] = cl->value;
191                 values_num++;
192         }
193
194         DBG ("swrite (fd = %i, values_num = %i)", fd, values_num);
195         if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0)
196         {
197                 DBG ("Writing to fd failed: %s", strerror (errno));
198                 syslog (LOG_ERR, "named plugin: Writing to fd failed: %s",
199                                 strerror (errno));
200                 return (-1);
201         }
202
203         if (values_num == 0)
204                 return (0);
205
206         DBG ("swrite (fd = %i, values = %p, size = %i)",
207                         fd, (void *) values, (int) (sizeof (int) * values_num));
208         if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
209         {
210                 DBG ("Writing to pipe failed: %s", strerror (errno));
211                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
212                                 strerror (errno));
213                 return (-1);
214         }
215
216         return (values_num);
217 }
218 #if NAMED_HAVE_CONFIG
219 static int named_config (char *key, char *value)
220 {
221 #if HAVE_LIBPCAP
222         if (strcasecmp (key, "Interface") == 0)
223         {
224                 if (pcap_device != NULL)
225                         free (pcap_device);
226                 if ((pcap_device = strdup (value)) == NULL)
227                         return (1);
228         }
229         else if (strcasecmp (key, "IgnoreSource") == 0)
230         {
231                 if (value != NULL)
232                         ignore_list_add_name (value);
233         }
234         else
235         {
236                 return (-1);
237         }
238
239         return (0);
240 #endif /* HAVE_LIBPCAP */
241 }
242 #endif /* NAMED_HAVE_CONFIG */
243
244 static void named_child_callback (const rfc1035_header_t *dns)
245 {
246         if (dns->qr == 0)
247         {
248                 /* This is a query */
249                 counter_list_add (&qtype_list,  dns->qtype,  1);
250         }
251         else
252         {
253                 /* This is a reply */
254                 counter_list_add (&rcode_list,  dns->rcode,  1);
255         }
256
257         /* FIXME: Are queries, replies or both interesting? */
258         counter_list_add (&opcode_list, dns->opcode, 1);
259 }
260
261 static void named_child_loop (void)
262 {
263         pcap_t *pcap_obj;
264         char    pcap_error[PCAP_ERRBUF_SIZE];
265         struct  bpf_program fp;
266
267         struct pollfd poll_fds[2];
268         int status;
269
270         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
271         DBG ("Creating PCAP object..");
272         pcap_obj = pcap_open_live (pcap_device,
273                         PCAP_SNAPLEN,
274                         0 /* Not promiscuous */,
275                         0 /* no read timeout */,
276                         pcap_error);
277         if (pcap_obj == NULL)
278         {
279                 syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s",
280                                 (pcap_device != NULL) ? pcap_device : "any",
281                                 pcap_error);
282                 close (pipe_fd);
283                 pipe_fd = -1;
284                 return;
285         }
286
287         memset (&fp, 0, sizeof (fp));
288         if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
289         {
290                 DBG ("pcap_compile failed");
291                 syslog (LOG_ERR, "named plugin: pcap_compile failed");
292                 close (pipe_fd);
293                 pipe_fd = -1;
294                 return;
295         }
296         if (pcap_setfilter (pcap_obj, &fp) < 0)
297         {
298                 DBG ("pcap_setfilter failed");
299                 syslog (LOG_ERR, "named plugin: pcap_setfilter failed");
300                 close (pipe_fd);
301                 pipe_fd = -1;
302                 return;
303         }
304
305         DBG ("PCAP object created.");
306
307         dnstop_set_pcap_obj (pcap_obj);
308         dnstop_set_callback (named_child_callback);
309
310         /* Set up pipe end */
311         poll_fds[0].fd = pipe_fd;
312         poll_fds[0].events = POLLOUT;
313
314         /* Set up pcap device */
315         poll_fds[1].fd = pcap_fileno (pcap_obj);
316         poll_fds[1].events = POLLIN | POLLPRI;
317
318         while (pipe_fd > 0)
319         {
320                 DBG ("poll (...)");
321                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
322
323                 if (status < 0)
324                 {
325                         syslog (LOG_ERR, "named plugin: poll(2) failed: %s",
326                                         strerror (errno));
327                         break;
328                 }
329
330                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
331                 {
332                         DBG ("Pipe closed. Exiting.");
333                         syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting.");
334                         break;
335                 }
336                 else if (poll_fds[0].revents & POLLOUT)
337                 {
338                         DBG ("Sending data..");
339                         counter_list_send (qtype_list, pipe_fd);
340                         counter_list_send (opcode_list, pipe_fd);
341                         counter_list_send (rcode_list, pipe_fd);
342                 }
343
344                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
345                 {
346                         DBG ("pcap-device closed. Exiting.");
347                         syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting.");
348                         break;
349                 }
350                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
351                 {
352                         status = pcap_dispatch (pcap_obj,
353                                         10 /* Only handle 10 packets at a time */,
354                                         handle_pcap /* callback */,
355                                         NULL /* Whatever this means.. */);
356                         if (status < 0)
357                         {
358                                 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
359                                 syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s",
360                                                 pcap_geterr (pcap_obj));
361                                 break;
362                         }
363                 }
364         } /* while (42) */
365
366         DBG ("child is exiting");
367
368         close (pipe_fd);
369         pipe_fd = -1;
370         pcap_close (pcap_obj);
371 } /* static void named_child_loop (void) */
372
373 static void named_init (void)
374 {
375 #if HAVE_LIBPCAP
376         int pipe_fds[2];
377         pid_t pid_child;
378
379         if (pipe (pipe_fds) != 0)
380         {
381                 syslog (LOG_ERR, "named plugin: pipe(2) failed: %s",
382                                 strerror (errno));
383                 return;
384         }
385
386         /* Fork off child */
387         pid_child = fork ();
388         if (pid_child < 0)
389         {
390                 syslog (LOG_ERR, "named plugin: fork(2) failed: %s",
391                                 strerror (errno));
392                 close (pipe_fds[0]);
393                 close (pipe_fds[1]);
394                 return;
395         }
396         else if (pid_child != 0)
397         {
398                 /* parent: Close the writing end, keep the reading end. */
399                 pipe_fd = pipe_fds[0];
400                 close (pipe_fds[1]);
401         }
402         else
403         {
404                 /* child: Close the reading end, keep the writing end. */
405                 pipe_fd = pipe_fds[1];
406                 close (pipe_fds[0]);
407
408                 named_child_loop ();
409                 exit (0);
410         }
411
412         /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
413 #endif
414 }
415
416 static void qtype_write (char *host, char *inst, char *val)
417 {
418         char file[512];
419         int status;
420
421         status = snprintf (file, 512, qtype_file, inst);
422         if (status < 1)
423                 return;
424         else if (status >= 512)
425                 return;
426
427         rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
428 }
429
430 static void rcode_write (char *host, char *inst, char *val)
431 {
432         char file[512];
433         int status;
434
435         status = snprintf (file, 512, rcode_file, inst);
436         if (status < 1)
437                 return;
438         else if (status >= 512)
439                 return;
440
441         rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
442 }
443
444 static void opcode_write (char *host, char *inst, char *val)
445 {
446         char file[512];
447         int status;
448
449         status = snprintf (file, 512, opcode_file, inst);
450         if (status < 1)
451                 return;
452         else if (status >= 512)
453                 return;
454
455         rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
456 }
457
458 static void qtype_submit (int qtype, unsigned int counter)
459 {
460         char inst[32];
461         char buffer[32];
462         int  status;
463
464         strncpy (inst, qtype_str (qtype), 32);
465         inst[31] = '\0';
466
467         status = snprintf (buffer, 32, "N:%u", counter);
468         if ((status < 1) || (status >= 32))
469                 return;
470
471         plugin_submit ("named_qtype", inst, buffer);
472 }
473
474 static void rcode_submit (int rcode, unsigned int counter)
475 {
476         char inst[32];
477         char buffer[32];
478         int  status;
479
480         strncpy (inst, rcode_str (rcode), 32);
481         inst[31] = '\0';
482
483         status = snprintf (buffer, 32, "N:%u", counter);
484         if ((status < 1) || (status >= 32))
485                 return;
486
487         plugin_submit ("named_rcode", inst, buffer);
488 }
489
490 static void opcode_submit (int opcode, unsigned int counter)
491 {
492         char inst[32];
493         char buffer[32];
494         int  status;
495
496         strncpy (inst, opcode_str (opcode), 32);
497         inst[31] = '\0';
498
499         status = snprintf (buffer, 32, "N:%u", counter);
500         if ((status < 1) || (status >= 32))
501                 return;
502
503         plugin_submit ("named_opcode", inst, buffer);
504 }
505
506 #if NAMED_HAVE_READ
507 static unsigned int named_read_array (unsigned int *values)
508 {
509         unsigned int values_num;
510
511         if (pipe_fd < 0)
512                 return (0);
513
514         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
515         {
516                 DBG ("Reading from the pipe failed: %s",
517                                 strerror (errno));
518                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
519                                 strerror (errno));
520                 pipe_fd = -1;
521                 return (0);
522         }
523         DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
524
525         assert (values_num <= T_MAX);
526
527         if (values_num == 0)
528                 return (0);
529
530         if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
531         {
532                 DBG ("Reading from the pipe failed: %s",
533                                 strerror (errno));
534                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
535                                 strerror (errno));
536                 pipe_fd = -1;
537                 return (0);
538         }
539
540         return (values_num);
541 }
542
543 static void named_read (void)
544 {
545         unsigned int values[2 * T_MAX];
546         unsigned int values_num;
547         int i;
548
549         if (pipe_fd < 0)
550                 return;
551
552         values_num = named_read_array (values);
553         for (i = 0; i < values_num; i++)
554         {
555                 DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
556                 qtype_submit (values[2 * i], values[(2 * i) + 1]);
557         }
558
559         values_num = named_read_array (values);
560         for (i = 0; i < values_num; i++)
561         {
562                 DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
563                 opcode_submit (values[2 * i], values[(2 * i) + 1]);
564         }
565
566         values_num = named_read_array (values);
567         for (i = 0; i < values_num; i++)
568         {
569                 DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
570                 rcode_submit (values[2 * i], values[(2 * i) + 1]);
571         }
572 }
573 #else /* if !NAMED_HAVE_READ */
574 # define named_read NULL
575 #endif
576
577 void module_register (void)
578 {
579         plugin_register (MODULE_NAME, named_init, named_read, NULL);
580         plugin_register ("named_qtype", NULL, NULL, qtype_write);
581         plugin_register ("named_rcode", NULL, NULL, rcode_write);
582         plugin_register ("named_opcode", NULL, NULL, opcode_write);
583         cf_register (MODULE_NAME, named_config, config_keys, config_keys_num);
584 }
585
586 #undef MODULE_NAME