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 **/
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 "named"
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 = "named/qtype-%s.rrd";
57 static char *opcode_file = "named/opcode-%s.rrd";
58 static char *rcode_file = "named/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 counter_list_t *qtype_list;
99 static counter_list_t *opcode_list;
100 static counter_list_t *rcode_list;
101 #endif
103 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
104 {
105 counter_list_t *entry;
107 DBG ("counter_list_search (list = %p, key = %u)",
108 (void *) *list, key);
110 for (entry = *list; entry != NULL; entry = entry->next)
111 if (entry->key == key)
112 break;
114 DBG ("return (%p)", (void *) entry);
115 return (entry);
116 }
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;
123 DBG ("counter_list_create (list = %p, key = %u, value = %u)",
124 (void *) *list, key, value);
126 entry = (counter_list_t *) malloc (sizeof (counter_list_t));
127 if (entry == NULL)
128 return (NULL);
130 memset (entry, 0, sizeof (counter_list_t));
131 entry->key = key;
132 entry->value = value;
134 if (*list == NULL)
135 {
136 *list = entry;
137 }
138 else
139 {
140 counter_list_t *last;
142 last = *list;
143 while (last->next != NULL)
144 last = last->next;
146 last->next = entry;
147 }
149 DBG ("return (%p)", (void *) entry);
150 return (entry);
151 }
153 static void counter_list_add (counter_list_t **list,
154 unsigned int key, unsigned int increment)
155 {
156 counter_list_t *entry;
158 DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
159 (void *) *list, key, increment);
161 entry = counter_list_search (list, key);
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 }
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;
180 if (fd < 0)
181 return (-1);
183 values_num = 0;
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 }
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 }
203 if (values_num == 0)
204 return (0);
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 }
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 }
239 return (0);
240 #endif /* HAVE_LIBPCAP */
241 }
242 #endif /* NAMED_HAVE_CONFIG */
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 }
257 /* FIXME: Are queries, replies or both interesting? */
258 counter_list_add (&opcode_list, dns->opcode, 1);
259 }
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;
267 struct pollfd poll_fds[2];
268 int status;
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 }
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 }
305 DBG ("PCAP object created.");
307 dnstop_set_pcap_obj (pcap_obj);
308 dnstop_set_callback (named_child_callback);
310 /* Set up pipe end */
311 poll_fds[0].fd = pipe_fd;
312 poll_fds[0].events = POLLOUT;
314 /* Set up pcap device */
315 poll_fds[1].fd = pcap_fileno (pcap_obj);
316 poll_fds[1].events = POLLIN | POLLPRI;
318 while (pipe_fd > 0)
319 {
320 DBG ("poll (...)");
321 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
323 if (status < 0)
324 {
325 syslog (LOG_ERR, "named plugin: poll(2) failed: %s",
326 strerror (errno));
327 break;
328 }
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 }
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) */
366 DBG ("child is exiting");
368 close (pipe_fd);
369 pipe_fd = -1;
370 pcap_close (pcap_obj);
371 } /* static void named_child_loop (void) */
373 static void named_init (void)
374 {
375 #if HAVE_LIBPCAP
376 int pipe_fds[2];
377 pid_t pid_child;
379 if (pipe (pipe_fds) != 0)
380 {
381 syslog (LOG_ERR, "named plugin: pipe(2) failed: %s",
382 strerror (errno));
383 return;
384 }
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]);
408 named_child_loop ();
409 exit (0);
410 }
412 /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
413 #endif
414 }
416 static void qtype_write (char *host, char *inst, char *val)
417 {
418 char file[512];
419 int status;
421 status = snprintf (file, 512, qtype_file, inst);
422 if (status < 1)
423 return;
424 else if (status >= 512)
425 return;
427 rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
428 }
430 static void rcode_write (char *host, char *inst, char *val)
431 {
432 char file[512];
433 int status;
435 status = snprintf (file, 512, rcode_file, inst);
436 if (status < 1)
437 return;
438 else if (status >= 512)
439 return;
441 rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
442 }
444 static void opcode_write (char *host, char *inst, char *val)
445 {
446 char file[512];
447 int status;
449 status = snprintf (file, 512, opcode_file, inst);
450 if (status < 1)
451 return;
452 else if (status >= 512)
453 return;
455 rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
456 }
458 static void qtype_submit (int qtype, unsigned int counter)
459 {
460 char inst[32];
461 char buffer[32];
462 int status;
464 strncpy (inst, qtype_str (qtype), 32);
465 inst[31] = '\0';
467 status = snprintf (buffer, 32, "N:%u", counter);
468 if ((status < 1) || (status >= 32))
469 return;
471 plugin_submit ("named_qtype", inst, buffer);
472 }
474 static void rcode_submit (int rcode, unsigned int counter)
475 {
476 char inst[32];
477 char buffer[32];
478 int status;
480 strncpy (inst, rcode_str (rcode), 32);
481 inst[31] = '\0';
483 status = snprintf (buffer, 32, "N:%u", counter);
484 if ((status < 1) || (status >= 32))
485 return;
487 plugin_submit ("named_rcode", inst, buffer);
488 }
490 static void opcode_submit (int opcode, unsigned int counter)
491 {
492 char inst[32];
493 char buffer[32];
494 int status;
496 strncpy (inst, opcode_str (opcode), 32);
497 inst[31] = '\0';
499 status = snprintf (buffer, 32, "N:%u", counter);
500 if ((status < 1) || (status >= 32))
501 return;
503 plugin_submit ("named_opcode", inst, buffer);
504 }
506 #if NAMED_HAVE_READ
507 static unsigned int named_read_array (unsigned int *values)
508 {
509 unsigned int values_num;
511 if (pipe_fd < 0)
512 return (0);
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);
525 assert (values_num <= T_MAX);
527 if (values_num == 0)
528 return (0);
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 }
540 return (values_num);
541 }
543 static void named_read (void)
544 {
545 unsigned int values[2 * T_MAX];
546 unsigned int values_num;
547 int i;
549 if (pipe_fd < 0)
550 return;
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 }
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 }
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
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 }
586 #undef MODULE_NAME