Merge branch 'collectd-4.6' into collectd-4.7
[collectd.git] / src / dns.c
1 /**
2  * collectd - src/dns.c
3  * Copyright (C) 2006,2007  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; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #define _BSD_SOURCE
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "configfile.h"
28
29 #include "utils_dns.h"
30 #include <pthread.h>
31 #include <pcap.h>
32 #include <poll.h>
33
34 /*
35  * Private data types
36  */
37 struct counter_list_s
38 {
39         unsigned int key;
40         unsigned int value;
41         struct counter_list_s *next;
42 };
43 typedef struct counter_list_s counter_list_t;
44
45 /*
46  * Private variables
47  */
48 static const char *config_keys[] =
49 {
50         "Interface",
51         "IgnoreSource",
52         NULL
53 };
54 static int config_keys_num = 2;
55
56 #define PCAP_SNAPLEN 1460
57 static char   *pcap_device = NULL;
58
59 static counter_t       tr_queries;
60 static counter_t       tr_responses;
61 static counter_list_t *qtype_list;
62 static counter_list_t *opcode_list;
63 static counter_list_t *rcode_list;
64
65 static pthread_t       listen_thread;
66 static int             listen_thread_init = 0;
67 /* The `traffic' mutex if for `tr_queries' and `tr_responses' */
68 static pthread_mutex_t traffic_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static pthread_mutex_t qtype_mutex   = PTHREAD_MUTEX_INITIALIZER;
70 static pthread_mutex_t opcode_mutex  = PTHREAD_MUTEX_INITIALIZER;
71 static pthread_mutex_t rcode_mutex   = PTHREAD_MUTEX_INITIALIZER;
72
73 /*
74  * Private functions
75  */
76 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
77 {
78         counter_list_t *entry;
79
80         DEBUG ("counter_list_search (list = %p, key = %u)",
81                         (void *) *list, key);
82
83         for (entry = *list; entry != NULL; entry = entry->next)
84                 if (entry->key == key)
85                         break;
86
87         DEBUG ("return (%p)", (void *) entry);
88         return (entry);
89 }
90
91 static counter_list_t *counter_list_create (counter_list_t **list,
92                 unsigned int key, unsigned int value)
93 {
94         counter_list_t *entry;
95
96         DEBUG ("counter_list_create (list = %p, key = %u, value = %u)",
97                         (void *) *list, key, value);
98
99         entry = (counter_list_t *) malloc (sizeof (counter_list_t));
100         if (entry == NULL)
101                 return (NULL);
102
103         memset (entry, 0, sizeof (counter_list_t));
104         entry->key = key;
105         entry->value = value;
106
107         if (*list == NULL)
108         {
109                 *list = entry;
110         }
111         else
112         {
113                 counter_list_t *last;
114
115                 last = *list;
116                 while (last->next != NULL)
117                         last = last->next;
118
119                 last->next = entry;
120         }
121
122         DEBUG ("return (%p)", (void *) entry);
123         return (entry);
124 }
125
126 static void counter_list_add (counter_list_t **list,
127                 unsigned int key, unsigned int increment)
128 {
129         counter_list_t *entry;
130
131         DEBUG ("counter_list_add (list = %p, key = %u, increment = %u)",
132                         (void *) *list, key, increment);
133
134         entry = counter_list_search (list, key);
135
136         if (entry != NULL)
137         {
138                 entry->value += increment;
139         }
140         else
141         {
142                 counter_list_create (list, key, increment);
143         }
144         DEBUG ("return ()");
145 }
146
147 static int dns_config (const char *key, const char *value)
148 {
149         if (strcasecmp (key, "Interface") == 0)
150         {
151                 if (pcap_device != NULL)
152                         free (pcap_device);
153                 if ((pcap_device = strdup (value)) == NULL)
154                         return (1);
155         }
156         else if (strcasecmp (key, "IgnoreSource") == 0)
157         {
158                 if (value != NULL)
159                         ignore_list_add_name (value);
160         }
161         else
162         {
163                 return (-1);
164         }
165
166         return (0);
167 }
168
169 static void dns_child_callback (const rfc1035_header_t *dns)
170 {
171         if (dns->qr == 0)
172         {
173                 /* This is a query */
174                 pthread_mutex_lock (&traffic_mutex);
175                 tr_queries += dns->length;
176                 pthread_mutex_unlock (&traffic_mutex);
177
178                 pthread_mutex_lock (&qtype_mutex);
179                 counter_list_add (&qtype_list,  dns->qtype,  1);
180                 pthread_mutex_unlock (&qtype_mutex);
181         }
182         else
183         {
184                 /* This is a reply */
185                 pthread_mutex_lock (&traffic_mutex);
186                 tr_responses += dns->length;
187                 pthread_mutex_unlock (&traffic_mutex);
188
189                 pthread_mutex_lock (&rcode_mutex);
190                 counter_list_add (&rcode_list,  dns->rcode,  1);
191                 pthread_mutex_unlock (&rcode_mutex);
192         }
193
194         /* FIXME: Are queries, replies or both interesting? */
195         pthread_mutex_lock (&opcode_mutex);
196         counter_list_add (&opcode_list, dns->opcode, 1);
197         pthread_mutex_unlock (&opcode_mutex);
198 }
199
200 static void *dns_child_loop (void __attribute__((unused)) *dummy)
201 {
202         pcap_t *pcap_obj;
203         char    pcap_error[PCAP_ERRBUF_SIZE];
204         struct  bpf_program fp;
205
206         int status;
207
208         /* Don't block any signals */
209         {
210                 sigset_t sigmask;
211                 sigemptyset (&sigmask);
212                 pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
213         }
214
215         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
216         DEBUG ("dns plugin: Creating PCAP object..");
217         pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any",
218                         PCAP_SNAPLEN,
219                         0 /* Not promiscuous */,
220                         interval_g,
221                         pcap_error);
222         if (pcap_obj == NULL)
223         {
224                 ERROR ("dns plugin: Opening interface `%s' "
225                                 "failed: %s",
226                                 (pcap_device != NULL) ? pcap_device : "any",
227                                 pcap_error);
228                 return (NULL);
229         }
230
231         memset (&fp, 0, sizeof (fp));
232         if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
233         {
234                 ERROR ("dns plugin: pcap_compile failed");
235                 return (NULL);
236         }
237         if (pcap_setfilter (pcap_obj, &fp) < 0)
238         {
239                 ERROR ("dns plugin: pcap_setfilter failed");
240                 return (NULL);
241         }
242
243         DEBUG ("PCAP object created.");
244
245         dnstop_set_pcap_obj (pcap_obj);
246         dnstop_set_callback (dns_child_callback);
247
248         status = pcap_loop (pcap_obj,
249                         -1 /* loop forever */,
250                         handle_pcap /* callback */,
251                         NULL /* Whatever this means.. */);
252         if (status < 0)
253                 ERROR ("dns plugin: Listener thread is exiting "
254                                 "abnormally: %s", pcap_geterr (pcap_obj));
255
256         DEBUG ("child is exiting");
257
258         pcap_close (pcap_obj);
259         listen_thread_init = 0;
260         pthread_exit (NULL);
261
262         return (NULL);
263 } /* static void dns_child_loop (void) */
264
265 static int dns_init (void)
266 {
267         /* clean up an old thread */
268         int status;
269
270         pthread_mutex_lock (&traffic_mutex);
271         tr_queries   = 0;
272         tr_responses = 0;
273         pthread_mutex_unlock (&traffic_mutex);
274
275         if (listen_thread_init != 0)
276                 return (-1);
277
278         status = pthread_create (&listen_thread, NULL, dns_child_loop,
279                         (void *) 0);
280         if (status != 0)
281         {
282                 char errbuf[1024];
283                 ERROR ("dns plugin: pthread_create failed: %s",
284                                 sstrerror (errno, errbuf, sizeof (errbuf)));
285                 return (-1);
286         }
287
288         listen_thread_init = 1;
289
290         return (0);
291 } /* int dns_init */
292
293 static void submit_counter (const char *type, const char *type_instance,
294                 counter_t value)
295 {
296         value_t values[1];
297         value_list_t vl = VALUE_LIST_INIT;
298
299         values[0].counter = value;
300
301         vl.values = values;
302         vl.values_len = 1;
303         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
304         sstrncpy (vl.plugin, "dns", sizeof (vl.plugin));
305         sstrncpy (vl.type, type, sizeof (vl.type));
306         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
307
308         plugin_dispatch_values (&vl);
309 } /* void submit_counter */
310
311 static void submit_octets (counter_t queries, counter_t responses)
312 {
313         value_t values[2];
314         value_list_t vl = VALUE_LIST_INIT;
315
316         values[0].counter = queries;
317         values[1].counter = responses;
318
319         vl.values = values;
320         vl.values_len = 2;
321         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
322         sstrncpy (vl.plugin, "dns", sizeof (vl.plugin));
323         sstrncpy (vl.type, "dns_octets", sizeof (vl.type));
324
325         plugin_dispatch_values (&vl);
326 } /* void submit_counter */
327
328 static int dns_read (void)
329 {
330         unsigned int keys[T_MAX];
331         unsigned int values[T_MAX];
332         int len;
333         int i;
334
335         counter_list_t *ptr;
336
337         pthread_mutex_lock (&traffic_mutex);
338         values[0] = tr_queries;
339         values[1] = tr_responses;
340         pthread_mutex_unlock (&traffic_mutex);
341
342         if ((values[0] != 0) || (values[1] != 0))
343                 submit_octets (values[0], values[1]);
344
345         pthread_mutex_lock (&qtype_mutex);
346         for (ptr = qtype_list, len = 0;
347                         (ptr != NULL) && (len < T_MAX);
348                         ptr = ptr->next, len++)
349         {
350                 keys[len]   = ptr->key;
351                 values[len] = ptr->value;
352         }
353         pthread_mutex_unlock (&qtype_mutex);
354
355         for (i = 0; i < len; i++)
356         {
357                 DEBUG ("qtype = %u; counter = %u;", keys[i], values[i]);
358                 submit_counter ("dns_qtype", qtype_str (keys[i]), values[i]);
359         }
360
361         pthread_mutex_lock (&opcode_mutex);
362         for (ptr = opcode_list, len = 0;
363                         (ptr != NULL) && (len < T_MAX);
364                         ptr = ptr->next, len++)
365         {
366                 keys[len]   = ptr->key;
367                 values[len] = ptr->value;
368         }
369         pthread_mutex_unlock (&opcode_mutex);
370
371         for (i = 0; i < len; i++)
372         {
373                 DEBUG ("opcode = %u; counter = %u;", keys[i], values[i]);
374                 submit_counter ("dns_opcode", opcode_str (keys[i]), values[i]);
375         }
376
377         pthread_mutex_lock (&rcode_mutex);
378         for (ptr = rcode_list, len = 0;
379                         (ptr != NULL) && (len < T_MAX);
380                         ptr = ptr->next, len++)
381         {
382                 keys[len]   = ptr->key;
383                 values[len] = ptr->value;
384         }
385         pthread_mutex_unlock (&rcode_mutex);
386
387         for (i = 0; i < len; i++)
388         {
389                 DEBUG ("rcode = %u; counter = %u;", keys[i], values[i]);
390                 submit_counter ("dns_rcode", rcode_str (keys[i]), values[i]);
391         }
392
393         return (0);
394 } /* int dns_read */
395
396 void module_register (void)
397 {
398         plugin_register_config ("dns", dns_config, config_keys, config_keys_num);
399         plugin_register_init ("dns", dns_init);
400         plugin_register_read ("dns", dns_read);
401 } /* void module_register */