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 **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_debug.h"
29 #if HAVE_SYS_POLL_H
30 # include <sys/poll.h>
31 #endif
33 #define MODULE_NAME "dns"
35 #if HAVE_LIBPCAP
36 # define NAMED_HAVE_CONFIG 1
37 #else
38 # define NAMED_HAVE_CONFIG 0
39 #endif
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
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;
56 static char *qtype_file = "dns/qtype-%s.rrd";
57 static char *opcode_file = "dns/opcode-%s.rrd";
58 static char *rcode_file = "dns/rcode-%s.rrd";
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;
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;
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;
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 */
93 #if HAVE_LIBPCAP
94 #define PCAP_SNAPLEN 1460
95 static char *pcap_device = NULL;
96 static int pipe_fd = -1;
98 static unsigned int tr_queries;
99 static unsigned int tr_responses;
100 static counter_list_t *qtype_list;
101 static counter_list_t *opcode_list;
102 static counter_list_t *rcode_list;
103 #endif
105 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
106 {
107 counter_list_t *entry;
109 DBG ("counter_list_search (list = %p, key = %u)",
110 (void *) *list, key);
112 for (entry = *list; entry != NULL; entry = entry->next)
113 if (entry->key == key)
114 break;
116 DBG ("return (%p)", (void *) entry);
117 return (entry);
118 }
120 static counter_list_t *counter_list_create (counter_list_t **list,
121 unsigned int key, unsigned int value)
122 {
123 counter_list_t *entry;
125 DBG ("counter_list_create (list = %p, key = %u, value = %u)",
126 (void *) *list, key, value);
128 entry = (counter_list_t *) malloc (sizeof (counter_list_t));
129 if (entry == NULL)
130 return (NULL);
132 memset (entry, 0, sizeof (counter_list_t));
133 entry->key = key;
134 entry->value = value;
136 if (*list == NULL)
137 {
138 *list = entry;
139 }
140 else
141 {
142 counter_list_t *last;
144 last = *list;
145 while (last->next != NULL)
146 last = last->next;
148 last->next = entry;
149 }
151 DBG ("return (%p)", (void *) entry);
152 return (entry);
153 }
155 static void counter_list_add (counter_list_t **list,
156 unsigned int key, unsigned int increment)
157 {
158 counter_list_t *entry;
160 DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
161 (void *) *list, key, increment);
163 entry = counter_list_search (list, key);
165 if (entry != NULL)
166 {
167 entry->value += increment;
168 }
169 else
170 {
171 counter_list_create (list, key, increment);
172 }
173 DBG ("return ()");
174 }
176 static int counter_list_send (counter_list_t *list, int fd)
177 {
178 counter_list_t *cl;
179 unsigned int values[2 * T_MAX];
180 unsigned int values_num;
182 if (fd < 0)
183 return (-1);
185 values_num = 0;
187 for (cl = list;
188 (cl != NULL) && (values_num < T_MAX);
189 cl = cl->next)
190 {
191 values[2 * values_num] = cl->key;
192 values[(2 * values_num) + 1] = cl->value;
193 values_num++;
194 }
196 DBG ("swrite (fd = %i, values_num = %i)", fd, values_num);
197 if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0)
198 {
199 DBG ("Writing to fd failed: %s", strerror (errno));
200 syslog (LOG_ERR, "dns plugin: Writing to fd failed: %s",
201 strerror (errno));
202 return (-1);
203 }
205 if (values_num == 0)
206 return (0);
208 DBG ("swrite (fd = %i, values = %p, size = %i)",
209 fd, (void *) values, (int) (sizeof (int) * values_num));
210 if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
211 {
212 DBG ("Writing to pipe failed: %s", strerror (errno));
213 syslog (LOG_ERR, "dns plugin: Writing to pipe failed: %s",
214 strerror (errno));
215 return (-1);
216 }
218 return (values_num);
219 }
220 #if NAMED_HAVE_CONFIG
221 static int dns_config (char *key, char *value)
222 {
223 #if HAVE_LIBPCAP
224 if (strcasecmp (key, "Interface") == 0)
225 {
226 if (pcap_device != NULL)
227 free (pcap_device);
228 if ((pcap_device = strdup (value)) == NULL)
229 return (1);
230 }
231 else if (strcasecmp (key, "IgnoreSource") == 0)
232 {
233 if (value != NULL)
234 ignore_list_add_name (value);
235 }
236 else
237 {
238 return (-1);
239 }
241 return (0);
242 #endif /* HAVE_LIBPCAP */
243 }
244 #endif /* NAMED_HAVE_CONFIG */
246 static void dns_child_callback (const rfc1035_header_t *dns)
247 {
248 if (dns->qr == 0)
249 {
250 /* This is a query */
251 tr_queries += dns->length;
252 counter_list_add (&qtype_list, dns->qtype, 1);
253 }
254 else
255 {
256 /* This is a reply */
257 tr_responses += dns->length;
258 counter_list_add (&rcode_list, dns->rcode, 1);
259 }
261 /* FIXME: Are queries, replies or both interesting? */
262 counter_list_add (&opcode_list, dns->opcode, 1);
263 }
265 static void dns_child_loop (void)
266 {
267 pcap_t *pcap_obj;
268 char pcap_error[PCAP_ERRBUF_SIZE];
269 struct bpf_program fp;
271 struct pollfd poll_fds[2];
272 int status;
274 /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
275 DBG ("Creating PCAP object..");
276 pcap_obj = pcap_open_live (pcap_device,
277 PCAP_SNAPLEN,
278 0 /* Not promiscuous */,
279 0 /* no read timeout */,
280 pcap_error);
281 if (pcap_obj == NULL)
282 {
283 syslog (LOG_ERR, "dns plugin: Opening interface `%s' failed: %s",
284 (pcap_device != NULL) ? pcap_device : "any",
285 pcap_error);
286 close (pipe_fd);
287 pipe_fd = -1;
288 return;
289 }
291 memset (&fp, 0, sizeof (fp));
292 if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
293 {
294 DBG ("pcap_compile failed");
295 syslog (LOG_ERR, "dns plugin: pcap_compile failed");
296 close (pipe_fd);
297 pipe_fd = -1;
298 return;
299 }
300 if (pcap_setfilter (pcap_obj, &fp) < 0)
301 {
302 DBG ("pcap_setfilter failed");
303 syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
304 close (pipe_fd);
305 pipe_fd = -1;
306 return;
307 }
309 DBG ("PCAP object created.");
311 dnstop_set_pcap_obj (pcap_obj);
312 dnstop_set_callback (dns_child_callback);
314 /* Set up pipe end */
315 poll_fds[0].fd = pipe_fd;
316 poll_fds[0].events = POLLOUT;
318 /* Set up pcap device */
319 poll_fds[1].fd = pcap_fileno (pcap_obj);
320 poll_fds[1].events = POLLIN | POLLPRI;
322 while (pipe_fd > 0)
323 {
324 DBG ("poll (...)");
325 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
327 if (status < 0)
328 {
329 syslog (LOG_ERR, "dns plugin: poll(2) failed: %s",
330 strerror (errno));
331 break;
332 }
334 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
335 {
336 DBG ("Pipe closed. Exiting.");
337 syslog (LOG_NOTICE, "dns plugin: Pipe closed. Exiting.");
338 break;
339 }
340 else if (poll_fds[0].revents & POLLOUT)
341 {
342 DBG ("Sending data..");
344 DBG ("swrite (pipe_fd = %i, tr_queries = %i)", pipe_fd, tr_queries);
345 if (swrite (pipe_fd, (const void *) &tr_queries, sizeof (tr_queries)) != 0)
346 {
347 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
348 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
349 strerror (errno));
350 return;
351 }
353 DBG ("swrite (pipe_fd = %i, tr_responses = %i)", pipe_fd, tr_responses);
354 if (swrite (pipe_fd, (const void *) &tr_responses, sizeof (tr_responses)) != 0)
355 {
356 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
357 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
358 strerror (errno));
359 return;
360 }
362 counter_list_send (qtype_list, pipe_fd);
363 counter_list_send (opcode_list, pipe_fd);
364 counter_list_send (rcode_list, pipe_fd);
365 }
367 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
368 {
369 DBG ("pcap-device closed. Exiting.");
370 syslog (LOG_ERR, "dns plugin: pcap-device closed. Exiting.");
371 break;
372 }
373 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
374 {
375 status = pcap_dispatch (pcap_obj,
376 10 /* Only handle 10 packets at a time */,
377 handle_pcap /* callback */,
378 NULL /* Whatever this means.. */);
379 if (status < 0)
380 {
381 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
382 syslog (LOG_ERR, "dns plugin: pcap_dispatch failed: %s",
383 pcap_geterr (pcap_obj));
384 break;
385 }
386 }
387 } /* while (42) */
389 DBG ("child is exiting");
391 close (pipe_fd);
392 pipe_fd = -1;
393 pcap_close (pcap_obj);
394 } /* static void dns_child_loop (void) */
396 static void dns_init (void)
397 {
398 #if HAVE_LIBPCAP
399 int pipe_fds[2];
400 pid_t pid_child;
402 tr_queries = 0;
403 tr_responses = 0;
405 if (pipe (pipe_fds) != 0)
406 {
407 syslog (LOG_ERR, "dns plugin: pipe(2) failed: %s",
408 strerror (errno));
409 return;
410 }
412 /* Fork off child */
413 pid_child = fork ();
414 if (pid_child < 0)
415 {
416 syslog (LOG_ERR, "dns plugin: fork(2) failed: %s",
417 strerror (errno));
418 close (pipe_fds[0]);
419 close (pipe_fds[1]);
420 return;
421 }
422 else if (pid_child != 0)
423 {
424 /* parent: Close the writing end, keep the reading end. */
425 pipe_fd = pipe_fds[0];
426 close (pipe_fds[1]);
427 }
428 else
429 {
430 /* child: Close the reading end, keep the writing end. */
431 pipe_fd = pipe_fds[1];
432 close (pipe_fds[0]);
434 dns_child_loop ();
435 exit (0);
436 }
438 /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
439 #endif
440 }
442 static void qtype_write (char *host, char *inst, char *val)
443 {
444 char file[512];
445 int status;
447 status = snprintf (file, 512, qtype_file, inst);
448 if (status < 1)
449 return;
450 else if (status >= 512)
451 return;
453 rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
454 }
456 static void rcode_write (char *host, char *inst, char *val)
457 {
458 char file[512];
459 int status;
461 status = snprintf (file, 512, rcode_file, inst);
462 if (status < 1)
463 return;
464 else if (status >= 512)
465 return;
467 rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
468 }
470 static void opcode_write (char *host, char *inst, char *val)
471 {
472 char file[512];
473 int status;
475 status = snprintf (file, 512, opcode_file, inst);
476 if (status < 1)
477 return;
478 else if (status >= 512)
479 return;
481 rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
482 }
484 static void qtype_submit (int qtype, unsigned int counter)
485 {
486 char inst[32];
487 char buffer[32];
488 int status;
490 strncpy (inst, qtype_str (qtype), 32);
491 inst[31] = '\0';
493 status = snprintf (buffer, 32, "N:%u", counter);
494 if ((status < 1) || (status >= 32))
495 return;
497 plugin_submit ("dns_qtype", inst, buffer);
498 }
500 static void rcode_submit (int rcode, unsigned int counter)
501 {
502 char inst[32];
503 char buffer[32];
504 int status;
506 strncpy (inst, rcode_str (rcode), 32);
507 inst[31] = '\0';
509 status = snprintf (buffer, 32, "N:%u", counter);
510 if ((status < 1) || (status >= 32))
511 return;
513 plugin_submit ("dns_rcode", inst, buffer);
514 }
516 static void opcode_submit (int opcode, unsigned int counter)
517 {
518 char inst[32];
519 char buffer[32];
520 int status;
522 strncpy (inst, opcode_str (opcode), 32);
523 inst[31] = '\0';
525 status = snprintf (buffer, 32, "N:%u", counter);
526 if ((status < 1) || (status >= 32))
527 return;
529 plugin_submit ("dns_opcode", inst, buffer);
530 }
532 #if NAMED_HAVE_READ
533 static unsigned int dns_read_array (unsigned int *values)
534 {
535 unsigned int values_num;
537 if (pipe_fd < 0)
538 return (0);
540 if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
541 {
542 DBG ("Reading from the pipe failed: %s",
543 strerror (errno));
544 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
545 strerror (errno));
546 pipe_fd = -1;
547 return (0);
548 }
549 DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
551 assert (values_num <= T_MAX);
553 if (values_num == 0)
554 return (0);
556 if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
557 {
558 DBG ("Reading from the pipe failed: %s",
559 strerror (errno));
560 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
561 strerror (errno));
562 pipe_fd = -1;
563 return (0);
564 }
566 return (values_num);
567 }
569 static void dns_read (void)
570 {
571 unsigned int values[2 * T_MAX];
572 unsigned int values_num;
573 int i;
575 if (pipe_fd < 0)
576 return;
578 if (sread (pipe_fd, (void *) &tr_queries, sizeof (tr_queries)) != 0)
579 {
580 DBG ("Reading from the pipe failed: %s",
581 strerror (errno));
582 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
583 strerror (errno));
584 pipe_fd = -1;
585 return;
586 }
587 DBG ("sread (pipe_fd = %i, tr_queries = %u)", pipe_fd, tr_queries);
589 if (sread (pipe_fd, (void *) &tr_responses, sizeof (tr_responses)) != 0)
590 {
591 DBG ("Reading from the pipe failed: %s",
592 strerror (errno));
593 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
594 strerror (errno));
595 pipe_fd = -1;
596 return;
597 }
598 DBG ("sread (pipe_fd = %i, tr_responses = %u)", pipe_fd, tr_responses);
600 values_num = dns_read_array (values);
601 for (i = 0; i < values_num; i++)
602 {
603 DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
604 qtype_submit (values[2 * i], values[(2 * i) + 1]);
605 }
607 values_num = dns_read_array (values);
608 for (i = 0; i < values_num; i++)
609 {
610 DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
611 opcode_submit (values[2 * i], values[(2 * i) + 1]);
612 }
614 values_num = dns_read_array (values);
615 for (i = 0; i < values_num; i++)
616 {
617 DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
618 rcode_submit (values[2 * i], values[(2 * i) + 1]);
619 }
620 }
621 #else /* if !NAMED_HAVE_READ */
622 # define dns_read NULL
623 #endif
625 void module_register (void)
626 {
627 plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
628 plugin_register ("dns_qtype", NULL, NULL, qtype_write);
629 plugin_register ("dns_rcode", NULL, NULL, rcode_write);
630 plugin_register ("dns_opcode", NULL, NULL, opcode_write);
631 cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
632 }
634 #undef MODULE_NAME