From: Florian Forster Date: Wed, 25 Oct 2006 13:43:10 +0000 (+0200) Subject: Renamed the `named'-plugin to `dns'-plugin. X-Git-Tag: collectd-3.11.0~42^2~6 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=fdfbf887b399bca44d6950d4c23e40debc5660e4;p=collectd.git Renamed the `named'-plugin to `dns'-plugin. --- diff --git a/configure.in b/configure.in index 25948181..48eb9015 100644 --- a/configure.in +++ b/configure.in @@ -272,7 +272,7 @@ AC_CHECK_HEADERS(kvm.h) # For debugging interface (variable number of arguments) AC_CHECK_HEADERS(stdarg.h) -# For the named plugin +# For the dns plugin AC_CHECK_HEADERS(arpa/nameser.h) dnl Checking for libraries @@ -945,13 +945,13 @@ AC_COLLECTD([cpu], [disable], [module], [cpu usage statistics]) AC_COLLECTD([cpufreq], [disable], [module], [system cpu frequency statistics]) AC_COLLECTD([disk], [disable], [module], [disk/partition statistics]) AC_COLLECTD([df], [disable], [module], [df statistics]) +AC_COLLECTD([dns], [disable], [module], [dns statistics]) AC_COLLECTD([quota], [enable], [module], [quota statistics (experimental)]) AC_COLLECTD([hddtemp], [disable], [module], [hdd temperature statistics]) AC_COLLECTD([load], [disable], [module], [system load statistics]) AC_COLLECTD([memory], [disable], [module], [memory statistics]) AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics]) AC_COLLECTD([mysql], [disable], [module], [mysql statistics]) -AC_COLLECTD([named], [disable], [module], [named statistics]) AC_COLLECTD([nfs], [disable], [module], [nfs statistics]) AC_COLLECTD([ntpd], [disable], [module], [nfs statistics]) AC_COLLECTD([ping], [disable], [module], [ping statistics]) @@ -996,12 +996,12 @@ Configuration: cpufreq . . . . . . $enable_cpufreq df . . . . . . . . $enable_df disk . . . . . . . $enable_disk + dns . . . . . . . . $enable_dns hddtemp . . . . . . $enable_hddtemp load . . . . . . . $enable_load memory . . . . . . $enable_memory multimeter . . . . $enable_multimeter mysql . . . . . . . $enable_mysql - named . . . . . . . $enable_named nfs . . . . . . . . $enable_nfs ntpd . . . . . . . $enable_ntpd ping . . . . . . . $enable_ping diff --git a/src/Makefile.am b/src/Makefile.am index dc45733e..246d0d43 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -236,15 +236,15 @@ collectd_LDADD += "-dlopen" mysql.la collectd_DEPENDENCIES += mysql.la endif -if BUILD_MODULE_NAMED -pkglib_LTLIBRARIES += named.la -named_la_SOURCES = named.c dnstop.c -named_la_LDFLAGS = -module -avoid-version +if BUILD_MODULE_DNS +pkglib_LTLIBRARIES += dns.la +dns_la_SOURCES = dns.c utils_dns.c +dns_la_LDFLAGS = -module -avoid-version if BUILD_WITH_LIBPCAP -named_la_LDFLAGS += -lpcap +dns_la_LDFLAGS += -lpcap endif -collectd_LDADD += "-dlopen" named.la -collectd_DEPENDENCIES += named.la +collectd_LDADD += "-dlopen" dns.la +collectd_DEPENDENCIES += dns.la endif if BUILD_MODULE_NFS diff --git a/src/dns.c b/src/dns.c new file mode 100644 index 00000000..7ab28141 --- /dev/null +++ b/src/dns.c @@ -0,0 +1,586 @@ +/** + * collectd - src/named.c + * Copyright (C) 2006 Florian octo Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_debug.h" + +#if HAVE_SYS_POLL_H +# include +#endif + +#define MODULE_NAME "named" + +#if HAVE_LIBPCAP +# define NAMED_HAVE_CONFIG 1 +#else +# define NAMED_HAVE_CONFIG 0 +#endif + +#if HAVE_LIBPCAP +# include "utils_dns.h" +# define NAMED_HAVE_READ 1 +#else +# define NAMED_HAVE_READ 0 +#endif + +struct counter_list_s +{ + unsigned int key; + unsigned int value; + struct counter_list_s *next; +}; +typedef struct counter_list_s counter_list_t; + +static char *qtype_file = "named/qtype-%s.rrd"; +static char *opcode_file = "named/opcode-%s.rrd"; +static char *rcode_file = "named/rcode-%s.rrd"; + +static char *qtype_ds_def[] = +{ + "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", + NULL +}; +static int qtype_ds_num = 1; + +static char *opcode_ds_def[] = +{ + "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", + NULL +}; +static int opcode_ds_num = 1; + +static char *rcode_ds_def[] = +{ + "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", + NULL +}; +static int rcode_ds_num = 1; + +#if NAMED_HAVE_CONFIG +#if HAVE_LIBPCAP +static char *config_keys[] = +{ + "Interface", + "IgnoreSource", + NULL +}; +static int config_keys_num = 2; +#endif /* HAVE_LIBPCAP */ +#endif /* NAMED_HAVE_CONFIG */ + +#if HAVE_LIBPCAP +#define PCAP_SNAPLEN 1460 +static char *pcap_device = NULL; +static int pipe_fd = -1; + +static counter_list_t *qtype_list; +static counter_list_t *opcode_list; +static counter_list_t *rcode_list; +#endif + +static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key) +{ + counter_list_t *entry; + + DBG ("counter_list_search (list = %p, key = %u)", + (void *) *list, key); + + for (entry = *list; entry != NULL; entry = entry->next) + if (entry->key == key) + break; + + DBG ("return (%p)", (void *) entry); + return (entry); +} + +static counter_list_t *counter_list_create (counter_list_t **list, + unsigned int key, unsigned int value) +{ + counter_list_t *entry; + + DBG ("counter_list_create (list = %p, key = %u, value = %u)", + (void *) *list, key, value); + + entry = (counter_list_t *) malloc (sizeof (counter_list_t)); + if (entry == NULL) + return (NULL); + + memset (entry, 0, sizeof (counter_list_t)); + entry->key = key; + entry->value = value; + + if (*list == NULL) + { + *list = entry; + } + else + { + counter_list_t *last; + + last = *list; + while (last->next != NULL) + last = last->next; + + last->next = entry; + } + + DBG ("return (%p)", (void *) entry); + return (entry); +} + +static void counter_list_add (counter_list_t **list, + unsigned int key, unsigned int increment) +{ + counter_list_t *entry; + + DBG ("counter_list_add (list = %p, key = %u, increment = %u)", + (void *) *list, key, increment); + + entry = counter_list_search (list, key); + + if (entry != NULL) + { + entry->value += increment; + } + else + { + counter_list_create (list, key, increment); + } + DBG ("return ()"); +} + +static int counter_list_send (counter_list_t *list, int fd) +{ + counter_list_t *cl; + unsigned int values[2 * T_MAX]; + unsigned int values_num; + + if (fd < 0) + return (-1); + + values_num = 0; + + for (cl = list; + (cl != NULL) && (values_num < T_MAX); + cl = cl->next) + { + values[2 * values_num] = cl->key; + values[(2 * values_num) + 1] = cl->value; + values_num++; + } + + DBG ("swrite (fd = %i, values_num = %i)", fd, values_num); + if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0) + { + DBG ("Writing to fd failed: %s", strerror (errno)); + syslog (LOG_ERR, "named plugin: Writing to fd failed: %s", + strerror (errno)); + return (-1); + } + + if (values_num == 0) + return (0); + + DBG ("swrite (fd = %i, values = %p, size = %i)", + fd, (void *) values, (int) (sizeof (int) * values_num)); + if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0) + { + DBG ("Writing to pipe failed: %s", strerror (errno)); + syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s", + strerror (errno)); + return (-1); + } + + return (values_num); +} +#if NAMED_HAVE_CONFIG +static int named_config (char *key, char *value) +{ +#if HAVE_LIBPCAP + if (strcasecmp (key, "Interface") == 0) + { + if (pcap_device != NULL) + free (pcap_device); + if ((pcap_device = strdup (value)) == NULL) + return (1); + } + else if (strcasecmp (key, "IgnoreSource") == 0) + { + if (value != NULL) + ignore_list_add_name (value); + } + else + { + return (-1); + } + + return (0); +#endif /* HAVE_LIBPCAP */ +} +#endif /* NAMED_HAVE_CONFIG */ + +static void named_child_callback (const rfc1035_header_t *dns) +{ + if (dns->qr == 0) + { + /* This is a query */ + counter_list_add (&qtype_list, dns->qtype, 1); + } + else + { + /* This is a reply */ + counter_list_add (&rcode_list, dns->rcode, 1); + } + + /* FIXME: Are queries, replies or both interesting? */ + counter_list_add (&opcode_list, dns->opcode, 1); +} + +static void named_child_loop (void) +{ + pcap_t *pcap_obj; + char pcap_error[PCAP_ERRBUF_SIZE]; + struct bpf_program fp; + + struct pollfd poll_fds[2]; + int status; + + /* Passing `pcap_device == NULL' is okay and the same as passign "any" */ + DBG ("Creating PCAP object.."); + pcap_obj = pcap_open_live (pcap_device, + PCAP_SNAPLEN, + 0 /* Not promiscuous */, + 0 /* no read timeout */, + pcap_error); + if (pcap_obj == NULL) + { + syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s", + (pcap_device != NULL) ? pcap_device : "any", + pcap_error); + close (pipe_fd); + pipe_fd = -1; + return; + } + + memset (&fp, 0, sizeof (fp)); + if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0) + { + DBG ("pcap_compile failed"); + syslog (LOG_ERR, "named plugin: pcap_compile failed"); + close (pipe_fd); + pipe_fd = -1; + return; + } + if (pcap_setfilter (pcap_obj, &fp) < 0) + { + DBG ("pcap_setfilter failed"); + syslog (LOG_ERR, "named plugin: pcap_setfilter failed"); + close (pipe_fd); + pipe_fd = -1; + return; + } + + DBG ("PCAP object created."); + + dnstop_set_pcap_obj (pcap_obj); + dnstop_set_callback (named_child_callback); + + /* Set up pipe end */ + poll_fds[0].fd = pipe_fd; + poll_fds[0].events = POLLOUT; + + /* Set up pcap device */ + poll_fds[1].fd = pcap_fileno (pcap_obj); + poll_fds[1].events = POLLIN | POLLPRI; + + while (pipe_fd > 0) + { + DBG ("poll (...)"); + status = poll (poll_fds, 2, -1 /* wait forever for a change */); + + if (status < 0) + { + syslog (LOG_ERR, "named plugin: poll(2) failed: %s", + strerror (errno)); + break; + } + + if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) + { + DBG ("Pipe closed. Exiting."); + syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting."); + break; + } + else if (poll_fds[0].revents & POLLOUT) + { + DBG ("Sending data.."); + counter_list_send (qtype_list, pipe_fd); + counter_list_send (opcode_list, pipe_fd); + counter_list_send (rcode_list, pipe_fd); + } + + if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) + { + DBG ("pcap-device closed. Exiting."); + syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting."); + break; + } + else if (poll_fds[1].revents & (POLLIN | POLLPRI)) + { + status = pcap_dispatch (pcap_obj, + 10 /* Only handle 10 packets at a time */, + handle_pcap /* callback */, + NULL /* Whatever this means.. */); + if (status < 0) + { + DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj)); + syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s", + pcap_geterr (pcap_obj)); + break; + } + } + } /* while (42) */ + + DBG ("child is exiting"); + + close (pipe_fd); + pipe_fd = -1; + pcap_close (pcap_obj); +} /* static void named_child_loop (void) */ + +static void named_init (void) +{ +#if HAVE_LIBPCAP + int pipe_fds[2]; + pid_t pid_child; + + if (pipe (pipe_fds) != 0) + { + syslog (LOG_ERR, "named plugin: pipe(2) failed: %s", + strerror (errno)); + return; + } + + /* Fork off child */ + pid_child = fork (); + if (pid_child < 0) + { + syslog (LOG_ERR, "named plugin: fork(2) failed: %s", + strerror (errno)); + close (pipe_fds[0]); + close (pipe_fds[1]); + return; + } + else if (pid_child != 0) + { + /* parent: Close the writing end, keep the reading end. */ + pipe_fd = pipe_fds[0]; + close (pipe_fds[1]); + } + else + { + /* child: Close the reading end, keep the writing end. */ + pipe_fd = pipe_fds[1]; + close (pipe_fds[0]); + + named_child_loop (); + exit (0); + } + + /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */ +#endif +} + +static void qtype_write (char *host, char *inst, char *val) +{ + char file[512]; + int status; + + status = snprintf (file, 512, qtype_file, inst); + if (status < 1) + return; + else if (status >= 512) + return; + + rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num); +} + +static void rcode_write (char *host, char *inst, char *val) +{ + char file[512]; + int status; + + status = snprintf (file, 512, rcode_file, inst); + if (status < 1) + return; + else if (status >= 512) + return; + + rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num); +} + +static void opcode_write (char *host, char *inst, char *val) +{ + char file[512]; + int status; + + status = snprintf (file, 512, opcode_file, inst); + if (status < 1) + return; + else if (status >= 512) + return; + + rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num); +} + +static void qtype_submit (int qtype, unsigned int counter) +{ + char inst[32]; + char buffer[32]; + int status; + + strncpy (inst, qtype_str (qtype), 32); + inst[31] = '\0'; + + status = snprintf (buffer, 32, "N:%u", counter); + if ((status < 1) || (status >= 32)) + return; + + plugin_submit ("named_qtype", inst, buffer); +} + +static void rcode_submit (int rcode, unsigned int counter) +{ + char inst[32]; + char buffer[32]; + int status; + + strncpy (inst, rcode_str (rcode), 32); + inst[31] = '\0'; + + status = snprintf (buffer, 32, "N:%u", counter); + if ((status < 1) || (status >= 32)) + return; + + plugin_submit ("named_rcode", inst, buffer); +} + +static void opcode_submit (int opcode, unsigned int counter) +{ + char inst[32]; + char buffer[32]; + int status; + + strncpy (inst, opcode_str (opcode), 32); + inst[31] = '\0'; + + status = snprintf (buffer, 32, "N:%u", counter); + if ((status < 1) || (status >= 32)) + return; + + plugin_submit ("named_opcode", inst, buffer); +} + +#if NAMED_HAVE_READ +static unsigned int named_read_array (unsigned int *values) +{ + unsigned int values_num; + + if (pipe_fd < 0) + return (0); + + if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0) + { + DBG ("Reading from the pipe failed: %s", + strerror (errno)); + syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s", + strerror (errno)); + pipe_fd = -1; + return (0); + } + DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num); + + assert (values_num <= T_MAX); + + if (values_num == 0) + return (0); + + if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0) + { + DBG ("Reading from the pipe failed: %s", + strerror (errno)); + syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s", + strerror (errno)); + pipe_fd = -1; + return (0); + } + + return (values_num); +} + +static void named_read (void) +{ + unsigned int values[2 * T_MAX]; + unsigned int values_num; + int i; + + if (pipe_fd < 0) + return; + + values_num = named_read_array (values); + for (i = 0; i < values_num; i++) + { + DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); + qtype_submit (values[2 * i], values[(2 * i) + 1]); + } + + values_num = named_read_array (values); + for (i = 0; i < values_num; i++) + { + DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); + opcode_submit (values[2 * i], values[(2 * i) + 1]); + } + + values_num = named_read_array (values); + for (i = 0; i < values_num; i++) + { + DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); + rcode_submit (values[2 * i], values[(2 * i) + 1]); + } +} +#else /* if !NAMED_HAVE_READ */ +# define named_read NULL +#endif + +void module_register (void) +{ + plugin_register (MODULE_NAME, named_init, named_read, NULL); + plugin_register ("named_qtype", NULL, NULL, qtype_write); + plugin_register ("named_rcode", NULL, NULL, rcode_write); + plugin_register ("named_opcode", NULL, NULL, opcode_write); + cf_register (MODULE_NAME, named_config, config_keys, config_keys_num); +} + +#undef MODULE_NAME diff --git a/src/dnstop.c b/src/dnstop.c deleted file mode 100644 index 306a7e0e..00000000 --- a/src/dnstop.c +++ /dev/null @@ -1,900 +0,0 @@ -/* - * collectd - src/dnstop.c - * Modifications Copyright (C) 2006 Florian octo Forster - * Copyright (C) 2002 The Measurement Factory, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of The Measurement Factory nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * The Measurement Factory, Inc. - * Florian octo Forster - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __APPLE__ -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define PCAP_SNAPLEN 1460 -#ifndef ETHER_HDR_LEN -#define ETHER_ADDR_LEN 6 -#define ETHER_TYPE_LEN 2 -#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) -#endif -#ifndef ETHERTYPE_8021Q -# define ETHERTYPE_8021Q 0x8100 -#endif -#ifndef ETHERTYPE_IPV6 -# define ETHERTYPE_IPV6 0x86DD -#endif - -#include -#ifndef PPP_ADDRESS_VAL -# define PPP_ADDRESS_VAL 0xff /* The address byte value */ -#endif -#ifndef PPP_CONTROL_VAL -# define PPP_CONTROL_VAL 0x03 /* The control byte value */ -#endif - -#ifdef __linux__ -#define uh_sport source -#define uh_dport dest -#endif - -#include "dnstop.h" - -/* - * Type definitions - */ -struct ip_list_s -{ - struct in6_addr addr; - void *data; - struct ip_list_s *next; -}; -typedef struct ip_list_s ip_list_t; - -typedef int (printer)(const char *, ...); - -/* - * flags/features for non-interactive mode - */ - -#ifndef T_A6 -#define T_A6 38 -#endif -#ifndef T_SRV -#define T_SRV 33 -#endif - -/* - * Global variables - */ -int qtype_counts[T_MAX]; -int opcode_counts[OP_MAX]; -int qclass_counts[C_MAX]; - -static pcap_t *pcap_obj = NULL; - -static ip_list_t *IgnoreList = NULL; - -static void (*Callback) (const rfc1035_header_t *) = NULL; - -static int query_count_intvl = 0; -static int query_count_total = 0; -#ifdef __OpenBSD__ -static struct bpf_timeval last_ts; -#else -static struct timeval last_ts; -#endif - -static int cmp_in6_addr (const struct in6_addr *a, - const struct in6_addr *b) -{ - int i; - - for (i = 0; i < 4; i++) - if (a->s6_addr32[i] != b->s6_addr32[i]) - break; - - if (i >= 4) - return (0); - - return (a->s6_addr32[i] > b->s6_addr32[i] ? 1 : -1); -} /* int cmp_addrinfo */ - -static inline int ignore_list_match (const struct in6_addr *addr) -{ - ip_list_t *ptr; - - for (ptr = IgnoreList; ptr != NULL; ptr = ptr->next) - if (cmp_in6_addr (addr, &ptr->addr) == 0) - return (1); - return (0); -} /* int ignore_list_match */ - -static void ignore_list_add (const struct in6_addr *addr) -{ - ip_list_t *new; - - if (ignore_list_match (addr) != 0) - return; - - new = malloc (sizeof (ip_list_t)); - if (new == NULL) - { - perror ("malloc"); - return; - } - - memcpy (&new->addr, addr, sizeof (struct in6_addr)); - new->next = IgnoreList; - - IgnoreList = new; -} /* void ignore_list_add */ - -void ignore_list_add_name (const char *name) -{ - struct addrinfo *ai_list; - struct addrinfo *ai_ptr; - struct in6_addr addr; - int status; - - status = getaddrinfo (name, NULL, NULL, &ai_list); - if (status != 0) - return; - - for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) - { - if (ai_ptr->ai_family == AF_INET) - { - addr.s6_addr32[0] = 0; - addr.s6_addr32[1] = 0; - addr.s6_addr32[2] = htonl (0x0000FFFF); - addr.s6_addr32[3] = ((struct sockaddr_in *) ai_ptr->ai_addr)->sin_addr.s_addr; - - ignore_list_add (&addr); - } - else - { - ignore_list_add (&((struct sockaddr_in6 *) ai_ptr->ai_addr)->sin6_addr); - } - } /* for */ - - freeaddrinfo (ai_list); -} - -static void in6_addr_from_buffer (struct in6_addr *ia, - const void *buf, size_t buf_len, - int family) -{ - memset (ia, 0, sizeof (struct in6_addr)); - if ((AF_INET == family) && (sizeof (uint32_t) == buf_len)) - { - ia->s6_addr32[2] = htonl (0x0000FFFF); - ia->s6_addr32[3] = *((uint32_t *) buf); - } - else if ((AF_INET6 == family) && (sizeof (struct in6_addr) == buf_len)) - { - memcpy (ia, buf, buf_len); - } -} /* void in6_addr_from_buffer */ - -void dnstop_set_pcap_obj (pcap_t *po) -{ - pcap_obj = po; -} - -void dnstop_set_callback (void (*cb) (const rfc1035_header_t *)) -{ - Callback = cb; -} - -#define RFC1035_MAXLABELSZ 63 -static int -rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns -) -{ - off_t no = 0; - unsigned char c; - size_t len; - assert(ns > 0); - do { - if ((*off) >= sz) - break; - c = *(buf + (*off)); - if (c > 191) { - /* blasted compression */ - unsigned short s; - off_t ptr; - memcpy(&s, buf + (*off), sizeof(s)); - s = ntohs(s); - (*off) += sizeof(s); - /* Sanity check */ - if ((*off) >= sz) - return 1; - ptr = s & 0x3FFF; - /* Make sure the pointer is inside this message */ - if (ptr >= sz) - return 2; - return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); - } else if (c > RFC1035_MAXLABELSZ) { - /* - * "(The 10 and 01 combinations are reserved for future use.)" - */ - break; - return 3; - } else { - (*off)++; - len = (size_t) c; - if (len == 0) - break; - if (len > (ns - 1)) - len = ns - 1; - if ((*off) + len > sz) /* message is too short */ - return 4; - memcpy(name + no, buf + (*off), len); - (*off) += len; - no += len; - *(name + (no++)) = '.'; - } - } while (c > 0); - *(name + no - 1) = '\0'; - /* make sure we didn't allow someone to overflow the name buffer */ - assert(no <= ns); - return 0; -} - -static int -handle_dns(const char *buf, int len, - const struct in6_addr *s_addr, - const struct in6_addr *d_addr) -{ - rfc1035_header_t qh; - uint16_t us; - off_t offset; - char *t; - int x; - - /* The DNS header is 12 bytes long */ - if (len < 12) - return 0; - - memcpy(&us, buf + 0, 2); - qh.id = ntohs(us); - - memcpy(&us, buf + 2, 2); - us = ntohs(us); - fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us); - qh.qr = (us >> 15) & 0x01; - qh.opcode = (us >> 11) & 0x0F; - qh.aa = (us >> 10) & 0x01; - qh.tc = (us >> 9) & 0x01; - qh.rd = (us >> 8) & 0x01; - qh.ra = (us >> 7) & 0x01; - qh.z = (us >> 6) & 0x01; - qh.ad = (us >> 5) & 0x01; - qh.cd = (us >> 4) & 0x01; - qh.rcode = us & 0x0F; - - memcpy(&us, buf + 4, 2); - qh.qdcount = ntohs(us); - - memcpy(&us, buf + 6, 2); - qh.ancount = ntohs(us); - - memcpy(&us, buf + 8, 2); - qh.nscount = ntohs(us); - - memcpy(&us, buf + 10, 2); - qh.arcount = ntohs(us); - - offset = 12; - memset(qh.qname, '\0', MAX_QNAME_SZ); - x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); - if (0 != x) - return 0; - if ('\0' == qh.qname[0]) - strcpy(qh.qname, "."); - while ((t = strchr(qh.qname, '\n'))) - *t = ' '; - while ((t = strchr(qh.qname, '\r'))) - *t = ' '; - for (t = qh.qname; *t; t++) - *t = tolower(*t); - - memcpy(&us, buf + offset, 2); - qh.qtype = ntohs(us); - memcpy(&us, buf + offset + 2, 2); - qh.qclass = ntohs(us); - - qh.length = (uint16_t) len; - - /* gather stats */ - qtype_counts[qh.qtype]++; - qclass_counts[qh.qclass]++; - opcode_counts[qh.opcode]++; - - if (Callback != NULL) - Callback (&qh); - - return 1; -} - -static int -handle_udp(const struct udphdr *udp, int len, - const struct in6_addr *s_addr, - const struct in6_addr *d_addr) -{ - char buf[PCAP_SNAPLEN]; - if ((ntohs (udp->uh_dport) != 53) - && (ntohs (udp->uh_sport) != 53)) - return 0; - memcpy(buf, udp + 1, len - sizeof(*udp)); - if (0 == handle_dns(buf, len - sizeof(*udp), s_addr, d_addr)) - return 0; - return 1; -} - -static int -handle_ipv6 (struct ip6_hdr *ipv6, int len) -{ - char buf[PCAP_SNAPLEN]; - int offset; - int nexthdr; - - struct in6_addr s_addr; - struct in6_addr d_addr; - uint16_t payload_len; - - offset = sizeof (struct ip6_hdr); - nexthdr = ipv6->ip6_nxt; - s_addr = ipv6->ip6_src; - d_addr = ipv6->ip6_dst; - payload_len = ntohs (ipv6->ip6_plen); - - if (ignore_list_match (&s_addr)) - return (0); - - /* Parse extension headers. This only handles the standard headers, as - * defined in RFC 2460, correctly. Fragments are discarded. */ - while ((IPPROTO_ROUTING == nexthdr) /* routing header */ - || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */ - || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */ - || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ - || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ - || (IPPROTO_AH == nexthdr) /* destination options. */ - || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */ - { - struct ip6_ext ext_hdr; - uint16_t ext_hdr_len; - - /* Catch broken packets */ - if ((offset + sizeof (struct ip6_ext)) > len) - return (0); - - /* Cannot handle fragments. */ - if (IPPROTO_FRAGMENT == nexthdr) - return (0); - - memcpy (&ext_hdr, (char *) ipv6 + offset, sizeof (struct ip6_ext)); - nexthdr = ext_hdr.ip6e_nxt; - ext_hdr_len = (8 * (ntohs (ext_hdr.ip6e_len) + 1)); - - /* This header is longer than the packets payload.. WTF? */ - if (ext_hdr_len > payload_len) - return (0); - - offset += ext_hdr_len; - payload_len -= ext_hdr_len; - } /* while */ - - /* Catch broken and empty packets */ - if (((offset + payload_len) > len) - || (payload_len == 0) - || (payload_len > PCAP_SNAPLEN)) - return (0); - - if (IPPROTO_UDP != nexthdr) - return (0); - - memcpy (buf, (char *) ipv6 + offset, payload_len); - if (handle_udp ((struct udphdr *) buf, payload_len, &s_addr, &d_addr) == 0) - return (0); - - return (1); /* Success */ -} /* int handle_ipv6 */ - -static int -handle_ip(const struct ip *ip, int len) -{ - char buf[PCAP_SNAPLEN]; - int offset = ip->ip_hl << 2; - struct in6_addr s_addr; - struct in6_addr d_addr; - - if (ip->ip_v == 6) - return (handle_ipv6 ((struct ip6_hdr *) ip, len)); - - in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET); - in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET); - if (ignore_list_match (&s_addr)) - return (0); - if (IPPROTO_UDP != ip->ip_p) - return 0; - memcpy(buf, (void *) ip + offset, len - offset); - if (0 == handle_udp((struct udphdr *) buf, len - offset, &s_addr, &d_addr)) - return 0; - return 1; -} - -static int -handle_ppp(const u_char * pkt, int len) -{ - char buf[PCAP_SNAPLEN]; - unsigned short us; - unsigned short proto; - if (len < 2) - return 0; - if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) { - pkt += 2; /* ACFC not used */ - len -= 2; - } - if (len < 2) - return 0; - if (*pkt % 2) { - proto = *pkt; /* PFC is used */ - pkt++; - len--; - } else { - memcpy(&us, pkt, sizeof(us)); - proto = ntohs(us); - pkt += 2; - len -= 2; - } - if (ETHERTYPE_IP != proto && PPP_IP != proto) - return 0; - memcpy(buf, pkt, len); - return handle_ip((struct ip *) buf, len); -} - -static int -handle_null(const u_char * pkt, int len) -{ - unsigned int family; - memcpy(&family, pkt, sizeof(family)); - if (AF_INET != family) - return 0; - return handle_ip((struct ip *) (pkt + 4), len - 4); -} - -#ifdef DLT_LOOP -static int -handle_loop(const u_char * pkt, int len) -{ - unsigned int family; - memcpy(&family, pkt, sizeof(family)); - if (AF_INET != ntohl(family)) - return 0; - return handle_ip((struct ip *) (pkt + 4), len - 4); -} - -#endif - -#ifdef DLT_RAW -static int -handle_raw(const u_char * pkt, int len) -{ - return handle_ip((struct ip *) pkt, len); -} - -#endif - -static int -handle_ether(const u_char * pkt, int len) -{ - char buf[PCAP_SNAPLEN]; - struct ether_header *e = (void *) pkt; - unsigned short etype = ntohs(e->ether_type); - if (len < ETHER_HDR_LEN) - return 0; - pkt += ETHER_HDR_LEN; - len -= ETHER_HDR_LEN; - if (ETHERTYPE_8021Q == etype) { - etype = ntohs(*(unsigned short *) (pkt + 2)); - pkt += 4; - len -= 4; - } - if ((ETHERTYPE_IP != etype) - && (ETHERTYPE_IPV6 != etype)) - return 0; - memcpy(buf, pkt, len); - if (ETHERTYPE_IPV6 == etype) - return (handle_ipv6 ((struct ip6_hdr *) buf, len)); - else - return handle_ip((struct ip *) buf, len); -} - -/* public function */ -void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt) -{ - int status; - - fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n", - (void *) udata, (void *) hdr, (void *) pkt, - hdr->caplen); - - if (hdr->caplen < ETHER_HDR_LEN) - return; - - switch (pcap_datalink (pcap_obj)) - { - case DLT_EN10MB: - status = handle_ether (pkt, hdr->caplen); - break; - case DLT_PPP: - status = handle_ppp (pkt, hdr->caplen); - break; -#ifdef DLT_LOOP - case DLT_LOOP: - status = handle_loop (pkt, hdr->caplen); - break; -#endif -#ifdef DLT_RAW - case DLT_RAW: - status = handle_raw (pkt, hdr->caplen); - break; -#endif - case DLT_NULL: - status = handle_null (pkt, hdr->caplen); - break; - - default: - fprintf (stderr, "unsupported data link type %d\n", - pcap_datalink(pcap_obj)); - status = 0; - break; - } /* switch (pcap_datalink(pcap_obj)) */ - - if (0 == status) - return; - - query_count_intvl++; - query_count_total++; - last_ts = hdr->ts; -} - -const char *qtype_str(int t) -{ - static char buf[32]; - switch (t) { - case ns_t_a: return ("A"); - case ns_t_ns: return ("NS"); - case ns_t_md: return ("MD"); - case ns_t_mf: return ("MF"); - case ns_t_cname: return ("CNAME"); - case ns_t_soa: return ("SOA"); - case ns_t_mb: return ("MB"); - case ns_t_mg: return ("MG"); - case ns_t_mr: return ("MR"); - case ns_t_null: return ("NULL"); - case ns_t_wks: return ("WKS"); - case ns_t_ptr: return ("PTR"); - case ns_t_hinfo: return ("HINFO"); - case ns_t_minfo: return ("MINFO"); - case ns_t_mx: return ("MX"); - case ns_t_txt: return ("TXT"); - case ns_t_rp: return ("RP"); - case ns_t_afsdb: return ("AFSDB"); - case ns_t_x25: return ("X25"); - case ns_t_isdn: return ("ISDN"); - case ns_t_rt: return ("RT"); - case ns_t_nsap: return ("NSAP"); - case ns_t_nsap_ptr: return ("NSAP-PTR"); - case ns_t_sig: return ("SIG"); - case ns_t_key: return ("KEY"); - case ns_t_px: return ("PX"); - case ns_t_gpos: return ("GPOS"); - case ns_t_aaaa: return ("AAAA"); - case ns_t_loc: return ("LOC"); - case ns_t_nxt: return ("NXT"); - case ns_t_eid: return ("EID"); - case ns_t_nimloc: return ("NIMLOC"); - case ns_t_srv: return ("SRV"); - case ns_t_atma: return ("ATMA"); - case ns_t_naptr: return ("NAPTR"); - case ns_t_kx: return ("KX"); - case ns_t_cert: return ("CERT"); - case ns_t_a6: return ("A6"); - case ns_t_dname: return ("DNAME"); - case ns_t_sink: return ("SINK"); - case ns_t_opt: return ("OPT"); - case ns_t_tsig: return ("TSIG"); - case ns_t_ixfr: return ("IXFR"); - case ns_t_axfr: return ("AXFR"); - case ns_t_mailb: return ("MAILB"); - case ns_t_maila: return ("MAILA"); - case ns_t_any: return ("ANY"); - case ns_t_zxfr: return ("ZXFR"); - default: - snprintf (buf, 32, "#%i", t); - buf[31] = '\0'; - return (buf); - }; /* switch (t) */ - /* NOTREACHED */ - return (NULL); -} - -const char *opcode_str (int o) -{ - static char buf[30]; - switch (o) { - case 0: - return "Query"; - break; - case 1: - return "Iquery"; - break; - case 2: - return "Status"; - break; - case 4: - return "Notify"; - break; - case 5: - return "Update"; - break; - default: - snprintf(buf, 30, "Opcode%d", o); - return buf; - } - /* NOTREACHED */ -} - -const char *rcode_str (int rcode) -{ - static char buf[32]; - switch (rcode) - { - case ns_r_noerror: return ("NOERROR"); - case ns_r_formerr: return ("FORMERR"); - case ns_r_servfail: return ("SERVFAIL"); - case ns_r_nxdomain: return ("NXDOMAIN"); - case ns_r_notimpl: return ("NOTIMPL"); - case ns_r_refused: return ("REFUSED"); - case ns_r_yxdomain: return ("YXDOMAIN"); - case ns_r_yxrrset: return ("YXRRSET"); - case ns_r_nxrrset: return ("NXRRSET"); - case ns_r_notauth: return ("NOTAUTH"); - case ns_r_notzone: return ("NOTZONE"); - case ns_r_max: return ("MAX"); - case ns_r_badsig: return ("BADSIG"); - case ns_r_badkey: return ("BADKEY"); - case ns_r_badtime: return ("BADTIME"); - default: - snprintf (buf, 32, "RCode%i", rcode); - buf[31] = '\0'; - return (buf); - } - /* Never reached */ - return (NULL); -} /* const char *rcode_str (int rcode) */ - -#if 0 -static int -main(int argc, char *argv[]) -{ - char errbuf[PCAP_ERRBUF_SIZE]; - int x; - struct stat sb; - int readfile_state = 0; - struct bpf_program fp; - - port53 = htons(53); - SubReport = Sources_report; - ignore_addr.s_addr = 0; - progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]); - srandom(time(NULL)); - ResetCounters(); - - while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) { - switch (x) { - case 'a': - anon_flag = 1; - break; - case 's': - sld_flag = 1; - break; - case 't': - nld_flag = 1; - break; - case 'p': - promisc_flag = 0; - break; - case 'b': - bpf_program_str = strdup(optarg); - break; - case 'i': - ignore_addr.s_addr = inet_addr(optarg); - break; - case 'f': - set_filter(optarg); - break; - default: - usage(); - break; - } - } - argc -= optind; - argv += optind; - - if (argc < 1) - usage(); - device = strdup(argv[0]); - - if (0 == stat(device, &sb)) - readfile_state = 1; - if (readfile_state) { - pcap_obj = pcap_open_offline(device, errbuf); - } else { - pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf); - } - if (NULL == pcap_obj) { - fprintf(stderr, "pcap_open_*: %s\n", errbuf); - exit(1); - } - - if (0 == isatty(1)) { - if (0 == readfile_state) { - fprintf(stderr, "Non-interactive mode requires savefile argument\n"); - exit(1); - } - interactive = 0; - print_func = printf; - } - - memset(&fp, '\0', sizeof(fp)); - x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0); - if (x < 0) { - fprintf(stderr, "pcap_compile failed\n"); - exit(1); - } - x = pcap_setfilter(pcap_obj, &fp); - if (x < 0) { - fprintf(stderr, "pcap_setfilter failed\n"); - exit(1); - } - - /* - * non-blocking call added for Mac OS X bugfix. Sent by Max Horn. - * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html - */ - x = pcap_setnonblock(pcap_obj, 1, errbuf); - if (x < 0) { - fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf); - exit(1); - } - - switch (pcap_datalink(pcap_obj)) { - case DLT_EN10MB: - handle_datalink = handle_ether; - break; -#if USE_PPP - case DLT_PPP: - handle_datalink = handle_ppp; - break; -#endif -#ifdef DLT_LOOP - case DLT_LOOP: - handle_datalink = handle_loop; - break; -#endif -#ifdef DLT_RAW - case DLT_RAW: - handle_datalink = handle_raw; - break; -#endif - case DLT_NULL: - handle_datalink = handle_null; - break; - default: - fprintf(stderr, "unsupported data link type %d\n", - pcap_datalink(pcap_obj)); - return 1; - break; - } - if (interactive) { - init_curses(); - while (0 == Quit) { - if (readfile_state < 2) { - /* - * On some OSes select() might return 0 even when - * there are packets to process. Thus, we always - * ignore its return value and just call pcap_dispatch() - * anyway. - */ - if (0 == readfile_state) /* interactive */ - pcap_select(pcap_obj, 1, 0); - x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL); - } - if (0 == x && 1 == readfile_state) { - /* block on keyboard until user quits */ - readfile_state++; - nodelay(w, 0); - } - keyboard(); - cron_pre(); - report(); - cron_post(); - } - endwin(); /* klin, Thu Nov 28 08:56:51 2002 */ - } else { - while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL)) - (void) 0; - cron_pre(); - Sources_report(); print_func("\n"); - Destinatioreport(); print_func("\n"); - Qtypes_report(); print_func("\n"); - Opcodes_report(); print_func("\n"); - Tld_report(); print_func("\n"); - Sld_report(); print_func("\n"); - Nld_report(); print_func("\n"); - SldBySource_report(); - } - - pcap_close(pcap_obj); - return 0; -} /* static int main(int argc, char *argv[]) */ -#endif diff --git a/src/dnstop.h b/src/dnstop.h deleted file mode 100644 index 8408147e..00000000 --- a/src/dnstop.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef COLLECTD_DNSTOP -#define COLLECTD_DNSTOP 1 -/* - * collectd - src/dnstop.c - * Copyright (C) 2006 Florian octo Forster - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of The Measurement Factory nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * Florian octo Forster - */ - -#include -#include - -#define T_MAX 65536 -#define OP_MAX 16 -#define C_MAX 65536 -#define MAX_QNAME_SZ 512 - -struct rfc1035_header_s { - uint16_t id; - unsigned int qr:1; - unsigned int opcode:4; - unsigned int aa:1; - unsigned int tc:1; - unsigned int rd:1; - unsigned int ra:1; - unsigned int z:1; - unsigned int ad:1; - unsigned int cd:1; - unsigned int rcode:4; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; - uint16_t qtype; - uint16_t qclass; - char qname[MAX_QNAME_SZ]; - uint16_t length; -}; -typedef struct rfc1035_header_s rfc1035_header_t; - -extern int qtype_counts[T_MAX]; -extern int opcode_counts[OP_MAX]; -extern int qclass_counts[C_MAX]; - -void dnstop_set_pcap_obj (pcap_t *po); -void dnstop_set_callback (void (*cb) (const rfc1035_header_t *)); - -void ignore_list_add_name (const char *name); -void handle_pcap (u_char * udata, const struct pcap_pkthdr *hdr, const u_char * pkt); - -const char *qtype_str(int t); -const char *opcode_str(int o); -const char *rcode_str (int r); - -#endif /* !COLLECTD_DNSTOP */ diff --git a/src/named.c b/src/named.c deleted file mode 100644 index 61778275..00000000 --- a/src/named.c +++ /dev/null @@ -1,586 +0,0 @@ -/** - * collectd - src/named.c - * Copyright (C) 2006 Florian octo Forster - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Authors: - * Florian octo Forster - **/ - -#include "collectd.h" -#include "common.h" -#include "plugin.h" -#include "configfile.h" -#include "utils_debug.h" - -#if HAVE_SYS_POLL_H -# include -#endif - -#define MODULE_NAME "named" - -#if HAVE_LIBPCAP -# define NAMED_HAVE_CONFIG 1 -#else -# define NAMED_HAVE_CONFIG 0 -#endif - -#if HAVE_LIBPCAP -# include "dnstop.h" -# define NAMED_HAVE_READ 1 -#else -# define NAMED_HAVE_READ 0 -#endif - -struct counter_list_s -{ - unsigned int key; - unsigned int value; - struct counter_list_s *next; -}; -typedef struct counter_list_s counter_list_t; - -static char *qtype_file = "named/qtype-%s.rrd"; -static char *opcode_file = "named/opcode-%s.rrd"; -static char *rcode_file = "named/rcode-%s.rrd"; - -static char *qtype_ds_def[] = -{ - "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", - NULL -}; -static int qtype_ds_num = 1; - -static char *opcode_ds_def[] = -{ - "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", - NULL -}; -static int opcode_ds_num = 1; - -static char *rcode_ds_def[] = -{ - "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535", - NULL -}; -static int rcode_ds_num = 1; - -#if NAMED_HAVE_CONFIG -#if HAVE_LIBPCAP -static char *config_keys[] = -{ - "Interface", - "IgnoreSource", - NULL -}; -static int config_keys_num = 2; -#endif /* HAVE_LIBPCAP */ -#endif /* NAMED_HAVE_CONFIG */ - -#if HAVE_LIBPCAP -#define PCAP_SNAPLEN 1460 -static char *pcap_device = NULL; -static int pipe_fd = -1; - -static counter_list_t *qtype_list; -static counter_list_t *opcode_list; -static counter_list_t *rcode_list; -#endif - -static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key) -{ - counter_list_t *entry; - - DBG ("counter_list_search (list = %p, key = %u)", - (void *) *list, key); - - for (entry = *list; entry != NULL; entry = entry->next) - if (entry->key == key) - break; - - DBG ("return (%p)", (void *) entry); - return (entry); -} - -static counter_list_t *counter_list_create (counter_list_t **list, - unsigned int key, unsigned int value) -{ - counter_list_t *entry; - - DBG ("counter_list_create (list = %p, key = %u, value = %u)", - (void *) *list, key, value); - - entry = (counter_list_t *) malloc (sizeof (counter_list_t)); - if (entry == NULL) - return (NULL); - - memset (entry, 0, sizeof (counter_list_t)); - entry->key = key; - entry->value = value; - - if (*list == NULL) - { - *list = entry; - } - else - { - counter_list_t *last; - - last = *list; - while (last->next != NULL) - last = last->next; - - last->next = entry; - } - - DBG ("return (%p)", (void *) entry); - return (entry); -} - -static void counter_list_add (counter_list_t **list, - unsigned int key, unsigned int increment) -{ - counter_list_t *entry; - - DBG ("counter_list_add (list = %p, key = %u, increment = %u)", - (void *) *list, key, increment); - - entry = counter_list_search (list, key); - - if (entry != NULL) - { - entry->value += increment; - } - else - { - counter_list_create (list, key, increment); - } - DBG ("return ()"); -} - -static int counter_list_send (counter_list_t *list, int fd) -{ - counter_list_t *cl; - unsigned int values[2 * T_MAX]; - unsigned int values_num; - - if (fd < 0) - return (-1); - - values_num = 0; - - for (cl = list; - (cl != NULL) && (values_num < T_MAX); - cl = cl->next) - { - values[2 * values_num] = cl->key; - values[(2 * values_num) + 1] = cl->value; - values_num++; - } - - DBG ("swrite (fd = %i, values_num = %i)", fd, values_num); - if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0) - { - DBG ("Writing to fd failed: %s", strerror (errno)); - syslog (LOG_ERR, "named plugin: Writing to fd failed: %s", - strerror (errno)); - return (-1); - } - - if (values_num == 0) - return (0); - - DBG ("swrite (fd = %i, values = %p, size = %i)", - fd, (void *) values, (int) (sizeof (int) * values_num)); - if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0) - { - DBG ("Writing to pipe failed: %s", strerror (errno)); - syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s", - strerror (errno)); - return (-1); - } - - return (values_num); -} -#if NAMED_HAVE_CONFIG -static int named_config (char *key, char *value) -{ -#if HAVE_LIBPCAP - if (strcasecmp (key, "Interface") == 0) - { - if (pcap_device != NULL) - free (pcap_device); - if ((pcap_device = strdup (value)) == NULL) - return (1); - } - else if (strcasecmp (key, "IgnoreSource") == 0) - { - if (value != NULL) - ignore_list_add_name (value); - } - else - { - return (-1); - } - - return (0); -#endif /* HAVE_LIBPCAP */ -} -#endif /* NAMED_HAVE_CONFIG */ - -static void named_child_callback (const rfc1035_header_t *dns) -{ - if (dns->qr == 0) - { - /* This is a query */ - counter_list_add (&qtype_list, dns->qtype, 1); - } - else - { - /* This is a reply */ - counter_list_add (&rcode_list, dns->rcode, 1); - } - - /* FIXME: Are queries, replies or both interesting? */ - counter_list_add (&opcode_list, dns->opcode, 1); -} - -static void named_child_loop (void) -{ - pcap_t *pcap_obj; - char pcap_error[PCAP_ERRBUF_SIZE]; - struct bpf_program fp; - - struct pollfd poll_fds[2]; - int status; - - /* Passing `pcap_device == NULL' is okay and the same as passign "any" */ - DBG ("Creating PCAP object.."); - pcap_obj = pcap_open_live (pcap_device, - PCAP_SNAPLEN, - 0 /* Not promiscuous */, - 0 /* no read timeout */, - pcap_error); - if (pcap_obj == NULL) - { - syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s", - (pcap_device != NULL) ? pcap_device : "any", - pcap_error); - close (pipe_fd); - pipe_fd = -1; - return; - } - - memset (&fp, 0, sizeof (fp)); - if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0) - { - DBG ("pcap_compile failed"); - syslog (LOG_ERR, "named plugin: pcap_compile failed"); - close (pipe_fd); - pipe_fd = -1; - return; - } - if (pcap_setfilter (pcap_obj, &fp) < 0) - { - DBG ("pcap_setfilter failed"); - syslog (LOG_ERR, "named plugin: pcap_setfilter failed"); - close (pipe_fd); - pipe_fd = -1; - return; - } - - DBG ("PCAP object created."); - - dnstop_set_pcap_obj (pcap_obj); - dnstop_set_callback (named_child_callback); - - /* Set up pipe end */ - poll_fds[0].fd = pipe_fd; - poll_fds[0].events = POLLOUT; - - /* Set up pcap device */ - poll_fds[1].fd = pcap_fileno (pcap_obj); - poll_fds[1].events = POLLIN | POLLPRI; - - while (pipe_fd > 0) - { - DBG ("poll (...)"); - status = poll (poll_fds, 2, -1 /* wait forever for a change */); - - if (status < 0) - { - syslog (LOG_ERR, "named plugin: poll(2) failed: %s", - strerror (errno)); - break; - } - - if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) - { - DBG ("Pipe closed. Exiting."); - syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting."); - break; - } - else if (poll_fds[0].revents & POLLOUT) - { - DBG ("Sending data.."); - counter_list_send (qtype_list, pipe_fd); - counter_list_send (opcode_list, pipe_fd); - counter_list_send (rcode_list, pipe_fd); - } - - if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) - { - DBG ("pcap-device closed. Exiting."); - syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting."); - break; - } - else if (poll_fds[1].revents & (POLLIN | POLLPRI)) - { - status = pcap_dispatch (pcap_obj, - 10 /* Only handle 10 packets at a time */, - handle_pcap /* callback */, - NULL /* Whatever this means.. */); - if (status < 0) - { - DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj)); - syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s", - pcap_geterr (pcap_obj)); - break; - } - } - } /* while (42) */ - - DBG ("child is exiting"); - - close (pipe_fd); - pipe_fd = -1; - pcap_close (pcap_obj); -} /* static void named_child_loop (void) */ - -static void named_init (void) -{ -#if HAVE_LIBPCAP - int pipe_fds[2]; - pid_t pid_child; - - if (pipe (pipe_fds) != 0) - { - syslog (LOG_ERR, "named plugin: pipe(2) failed: %s", - strerror (errno)); - return; - } - - /* Fork off child */ - pid_child = fork (); - if (pid_child < 0) - { - syslog (LOG_ERR, "named plugin: fork(2) failed: %s", - strerror (errno)); - close (pipe_fds[0]); - close (pipe_fds[1]); - return; - } - else if (pid_child != 0) - { - /* parent: Close the writing end, keep the reading end. */ - pipe_fd = pipe_fds[0]; - close (pipe_fds[1]); - } - else - { - /* child: Close the reading end, keep the writing end. */ - pipe_fd = pipe_fds[1]; - close (pipe_fds[0]); - - named_child_loop (); - exit (0); - } - - /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */ -#endif -} - -static void qtype_write (char *host, char *inst, char *val) -{ - char file[512]; - int status; - - status = snprintf (file, 512, qtype_file, inst); - if (status < 1) - return; - else if (status >= 512) - return; - - rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num); -} - -static void rcode_write (char *host, char *inst, char *val) -{ - char file[512]; - int status; - - status = snprintf (file, 512, rcode_file, inst); - if (status < 1) - return; - else if (status >= 512) - return; - - rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num); -} - -static void opcode_write (char *host, char *inst, char *val) -{ - char file[512]; - int status; - - status = snprintf (file, 512, opcode_file, inst); - if (status < 1) - return; - else if (status >= 512) - return; - - rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num); -} - -static void qtype_submit (int qtype, unsigned int counter) -{ - char inst[32]; - char buffer[32]; - int status; - - strncpy (inst, qtype_str (qtype), 32); - inst[31] = '\0'; - - status = snprintf (buffer, 32, "N:%u", counter); - if ((status < 1) || (status >= 32)) - return; - - plugin_submit ("named_qtype", inst, buffer); -} - -static void rcode_submit (int rcode, unsigned int counter) -{ - char inst[32]; - char buffer[32]; - int status; - - strncpy (inst, rcode_str (rcode), 32); - inst[31] = '\0'; - - status = snprintf (buffer, 32, "N:%u", counter); - if ((status < 1) || (status >= 32)) - return; - - plugin_submit ("named_rcode", inst, buffer); -} - -static void opcode_submit (int opcode, unsigned int counter) -{ - char inst[32]; - char buffer[32]; - int status; - - strncpy (inst, opcode_str (opcode), 32); - inst[31] = '\0'; - - status = snprintf (buffer, 32, "N:%u", counter); - if ((status < 1) || (status >= 32)) - return; - - plugin_submit ("named_opcode", inst, buffer); -} - -#if NAMED_HAVE_READ -static unsigned int named_read_array (unsigned int *values) -{ - unsigned int values_num; - - if (pipe_fd < 0) - return (0); - - if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0) - { - DBG ("Reading from the pipe failed: %s", - strerror (errno)); - syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s", - strerror (errno)); - pipe_fd = -1; - return (0); - } - DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num); - - assert (values_num <= T_MAX); - - if (values_num == 0) - return (0); - - if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0) - { - DBG ("Reading from the pipe failed: %s", - strerror (errno)); - syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s", - strerror (errno)); - pipe_fd = -1; - return (0); - } - - return (values_num); -} - -static void named_read (void) -{ - unsigned int values[2 * T_MAX]; - unsigned int values_num; - int i; - - if (pipe_fd < 0) - return; - - values_num = named_read_array (values); - for (i = 0; i < values_num; i++) - { - DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); - qtype_submit (values[2 * i], values[(2 * i) + 1]); - } - - values_num = named_read_array (values); - for (i = 0; i < values_num; i++) - { - DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); - opcode_submit (values[2 * i], values[(2 * i) + 1]); - } - - values_num = named_read_array (values); - for (i = 0; i < values_num; i++) - { - DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]); - rcode_submit (values[2 * i], values[(2 * i) + 1]); - } -} -#else /* if !NAMED_HAVE_READ */ -# define named_read NULL -#endif - -void module_register (void) -{ - plugin_register (MODULE_NAME, named_init, named_read, NULL); - plugin_register ("named_qtype", NULL, NULL, qtype_write); - plugin_register ("named_rcode", NULL, NULL, rcode_write); - plugin_register ("named_opcode", NULL, NULL, opcode_write); - cf_register (MODULE_NAME, named_config, config_keys, config_keys_num); -} - -#undef MODULE_NAME diff --git a/src/utils_dns.c b/src/utils_dns.c new file mode 100644 index 00000000..19afe55e --- /dev/null +++ b/src/utils_dns.c @@ -0,0 +1,900 @@ +/* + * collectd - src/utils_dns.c + * Modifications Copyright (C) 2006 Florian octo Forster + * Copyright (C) 2002 The Measurement Factory, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of The Measurement Factory nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * The Measurement Factory, Inc. + * Florian octo Forster + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define PCAP_SNAPLEN 1460 +#ifndef ETHER_HDR_LEN +#define ETHER_ADDR_LEN 6 +#define ETHER_TYPE_LEN 2 +#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) +#endif +#ifndef ETHERTYPE_8021Q +# define ETHERTYPE_8021Q 0x8100 +#endif +#ifndef ETHERTYPE_IPV6 +# define ETHERTYPE_IPV6 0x86DD +#endif + +#include +#ifndef PPP_ADDRESS_VAL +# define PPP_ADDRESS_VAL 0xff /* The address byte value */ +#endif +#ifndef PPP_CONTROL_VAL +# define PPP_CONTROL_VAL 0x03 /* The control byte value */ +#endif + +#ifdef __linux__ +#define uh_sport source +#define uh_dport dest +#endif + +#include "utils_dns.h" + +/* + * Type definitions + */ +struct ip_list_s +{ + struct in6_addr addr; + void *data; + struct ip_list_s *next; +}; +typedef struct ip_list_s ip_list_t; + +typedef int (printer)(const char *, ...); + +/* + * flags/features for non-interactive mode + */ + +#ifndef T_A6 +#define T_A6 38 +#endif +#ifndef T_SRV +#define T_SRV 33 +#endif + +/* + * Global variables + */ +int qtype_counts[T_MAX]; +int opcode_counts[OP_MAX]; +int qclass_counts[C_MAX]; + +static pcap_t *pcap_obj = NULL; + +static ip_list_t *IgnoreList = NULL; + +static void (*Callback) (const rfc1035_header_t *) = NULL; + +static int query_count_intvl = 0; +static int query_count_total = 0; +#ifdef __OpenBSD__ +static struct bpf_timeval last_ts; +#else +static struct timeval last_ts; +#endif + +static int cmp_in6_addr (const struct in6_addr *a, + const struct in6_addr *b) +{ + int i; + + for (i = 0; i < 4; i++) + if (a->s6_addr32[i] != b->s6_addr32[i]) + break; + + if (i >= 4) + return (0); + + return (a->s6_addr32[i] > b->s6_addr32[i] ? 1 : -1); +} /* int cmp_addrinfo */ + +static inline int ignore_list_match (const struct in6_addr *addr) +{ + ip_list_t *ptr; + + for (ptr = IgnoreList; ptr != NULL; ptr = ptr->next) + if (cmp_in6_addr (addr, &ptr->addr) == 0) + return (1); + return (0); +} /* int ignore_list_match */ + +static void ignore_list_add (const struct in6_addr *addr) +{ + ip_list_t *new; + + if (ignore_list_match (addr) != 0) + return; + + new = malloc (sizeof (ip_list_t)); + if (new == NULL) + { + perror ("malloc"); + return; + } + + memcpy (&new->addr, addr, sizeof (struct in6_addr)); + new->next = IgnoreList; + + IgnoreList = new; +} /* void ignore_list_add */ + +void ignore_list_add_name (const char *name) +{ + struct addrinfo *ai_list; + struct addrinfo *ai_ptr; + struct in6_addr addr; + int status; + + status = getaddrinfo (name, NULL, NULL, &ai_list); + if (status != 0) + return; + + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + if (ai_ptr->ai_family == AF_INET) + { + addr.s6_addr32[0] = 0; + addr.s6_addr32[1] = 0; + addr.s6_addr32[2] = htonl (0x0000FFFF); + addr.s6_addr32[3] = ((struct sockaddr_in *) ai_ptr->ai_addr)->sin_addr.s_addr; + + ignore_list_add (&addr); + } + else + { + ignore_list_add (&((struct sockaddr_in6 *) ai_ptr->ai_addr)->sin6_addr); + } + } /* for */ + + freeaddrinfo (ai_list); +} + +static void in6_addr_from_buffer (struct in6_addr *ia, + const void *buf, size_t buf_len, + int family) +{ + memset (ia, 0, sizeof (struct in6_addr)); + if ((AF_INET == family) && (sizeof (uint32_t) == buf_len)) + { + ia->s6_addr32[2] = htonl (0x0000FFFF); + ia->s6_addr32[3] = *((uint32_t *) buf); + } + else if ((AF_INET6 == family) && (sizeof (struct in6_addr) == buf_len)) + { + memcpy (ia, buf, buf_len); + } +} /* void in6_addr_from_buffer */ + +void dnstop_set_pcap_obj (pcap_t *po) +{ + pcap_obj = po; +} + +void dnstop_set_callback (void (*cb) (const rfc1035_header_t *)) +{ + Callback = cb; +} + +#define RFC1035_MAXLABELSZ 63 +static int +rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns +) +{ + off_t no = 0; + unsigned char c; + size_t len; + assert(ns > 0); + do { + if ((*off) >= sz) + break; + c = *(buf + (*off)); + if (c > 191) { + /* blasted compression */ + unsigned short s; + off_t ptr; + memcpy(&s, buf + (*off), sizeof(s)); + s = ntohs(s); + (*off) += sizeof(s); + /* Sanity check */ + if ((*off) >= sz) + return 1; + ptr = s & 0x3FFF; + /* Make sure the pointer is inside this message */ + if (ptr >= sz) + return 2; + return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); + } else if (c > RFC1035_MAXLABELSZ) { + /* + * "(The 10 and 01 combinations are reserved for future use.)" + */ + break; + return 3; + } else { + (*off)++; + len = (size_t) c; + if (len == 0) + break; + if (len > (ns - 1)) + len = ns - 1; + if ((*off) + len > sz) /* message is too short */ + return 4; + memcpy(name + no, buf + (*off), len); + (*off) += len; + no += len; + *(name + (no++)) = '.'; + } + } while (c > 0); + *(name + no - 1) = '\0'; + /* make sure we didn't allow someone to overflow the name buffer */ + assert(no <= ns); + return 0; +} + +static int +handle_dns(const char *buf, int len, + const struct in6_addr *s_addr, + const struct in6_addr *d_addr) +{ + rfc1035_header_t qh; + uint16_t us; + off_t offset; + char *t; + int x; + + /* The DNS header is 12 bytes long */ + if (len < 12) + return 0; + + memcpy(&us, buf + 0, 2); + qh.id = ntohs(us); + + memcpy(&us, buf + 2, 2); + us = ntohs(us); + fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us); + qh.qr = (us >> 15) & 0x01; + qh.opcode = (us >> 11) & 0x0F; + qh.aa = (us >> 10) & 0x01; + qh.tc = (us >> 9) & 0x01; + qh.rd = (us >> 8) & 0x01; + qh.ra = (us >> 7) & 0x01; + qh.z = (us >> 6) & 0x01; + qh.ad = (us >> 5) & 0x01; + qh.cd = (us >> 4) & 0x01; + qh.rcode = us & 0x0F; + + memcpy(&us, buf + 4, 2); + qh.qdcount = ntohs(us); + + memcpy(&us, buf + 6, 2); + qh.ancount = ntohs(us); + + memcpy(&us, buf + 8, 2); + qh.nscount = ntohs(us); + + memcpy(&us, buf + 10, 2); + qh.arcount = ntohs(us); + + offset = 12; + memset(qh.qname, '\0', MAX_QNAME_SZ); + x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); + if (0 != x) + return 0; + if ('\0' == qh.qname[0]) + strcpy(qh.qname, "."); + while ((t = strchr(qh.qname, '\n'))) + *t = ' '; + while ((t = strchr(qh.qname, '\r'))) + *t = ' '; + for (t = qh.qname; *t; t++) + *t = tolower(*t); + + memcpy(&us, buf + offset, 2); + qh.qtype = ntohs(us); + memcpy(&us, buf + offset + 2, 2); + qh.qclass = ntohs(us); + + qh.length = (uint16_t) len; + + /* gather stats */ + qtype_counts[qh.qtype]++; + qclass_counts[qh.qclass]++; + opcode_counts[qh.opcode]++; + + if (Callback != NULL) + Callback (&qh); + + return 1; +} + +static int +handle_udp(const struct udphdr *udp, int len, + const struct in6_addr *s_addr, + const struct in6_addr *d_addr) +{ + char buf[PCAP_SNAPLEN]; + if ((ntohs (udp->uh_dport) != 53) + && (ntohs (udp->uh_sport) != 53)) + return 0; + memcpy(buf, udp + 1, len - sizeof(*udp)); + if (0 == handle_dns(buf, len - sizeof(*udp), s_addr, d_addr)) + return 0; + return 1; +} + +static int +handle_ipv6 (struct ip6_hdr *ipv6, int len) +{ + char buf[PCAP_SNAPLEN]; + int offset; + int nexthdr; + + struct in6_addr s_addr; + struct in6_addr d_addr; + uint16_t payload_len; + + offset = sizeof (struct ip6_hdr); + nexthdr = ipv6->ip6_nxt; + s_addr = ipv6->ip6_src; + d_addr = ipv6->ip6_dst; + payload_len = ntohs (ipv6->ip6_plen); + + if (ignore_list_match (&s_addr)) + return (0); + + /* Parse extension headers. This only handles the standard headers, as + * defined in RFC 2460, correctly. Fragments are discarded. */ + while ((IPPROTO_ROUTING == nexthdr) /* routing header */ + || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */ + || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */ + || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ + || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */ + || (IPPROTO_AH == nexthdr) /* destination options. */ + || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */ + { + struct ip6_ext ext_hdr; + uint16_t ext_hdr_len; + + /* Catch broken packets */ + if ((offset + sizeof (struct ip6_ext)) > len) + return (0); + + /* Cannot handle fragments. */ + if (IPPROTO_FRAGMENT == nexthdr) + return (0); + + memcpy (&ext_hdr, (char *) ipv6 + offset, sizeof (struct ip6_ext)); + nexthdr = ext_hdr.ip6e_nxt; + ext_hdr_len = (8 * (ntohs (ext_hdr.ip6e_len) + 1)); + + /* This header is longer than the packets payload.. WTF? */ + if (ext_hdr_len > payload_len) + return (0); + + offset += ext_hdr_len; + payload_len -= ext_hdr_len; + } /* while */ + + /* Catch broken and empty packets */ + if (((offset + payload_len) > len) + || (payload_len == 0) + || (payload_len > PCAP_SNAPLEN)) + return (0); + + if (IPPROTO_UDP != nexthdr) + return (0); + + memcpy (buf, (char *) ipv6 + offset, payload_len); + if (handle_udp ((struct udphdr *) buf, payload_len, &s_addr, &d_addr) == 0) + return (0); + + return (1); /* Success */ +} /* int handle_ipv6 */ + +static int +handle_ip(const struct ip *ip, int len) +{ + char buf[PCAP_SNAPLEN]; + int offset = ip->ip_hl << 2; + struct in6_addr s_addr; + struct in6_addr d_addr; + + if (ip->ip_v == 6) + return (handle_ipv6 ((struct ip6_hdr *) ip, len)); + + in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET); + in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET); + if (ignore_list_match (&s_addr)) + return (0); + if (IPPROTO_UDP != ip->ip_p) + return 0; + memcpy(buf, (void *) ip + offset, len - offset); + if (0 == handle_udp((struct udphdr *) buf, len - offset, &s_addr, &d_addr)) + return 0; + return 1; +} + +static int +handle_ppp(const u_char * pkt, int len) +{ + char buf[PCAP_SNAPLEN]; + unsigned short us; + unsigned short proto; + if (len < 2) + return 0; + if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) { + pkt += 2; /* ACFC not used */ + len -= 2; + } + if (len < 2) + return 0; + if (*pkt % 2) { + proto = *pkt; /* PFC is used */ + pkt++; + len--; + } else { + memcpy(&us, pkt, sizeof(us)); + proto = ntohs(us); + pkt += 2; + len -= 2; + } + if (ETHERTYPE_IP != proto && PPP_IP != proto) + return 0; + memcpy(buf, pkt, len); + return handle_ip((struct ip *) buf, len); +} + +static int +handle_null(const u_char * pkt, int len) +{ + unsigned int family; + memcpy(&family, pkt, sizeof(family)); + if (AF_INET != family) + return 0; + return handle_ip((struct ip *) (pkt + 4), len - 4); +} + +#ifdef DLT_LOOP +static int +handle_loop(const u_char * pkt, int len) +{ + unsigned int family; + memcpy(&family, pkt, sizeof(family)); + if (AF_INET != ntohl(family)) + return 0; + return handle_ip((struct ip *) (pkt + 4), len - 4); +} + +#endif + +#ifdef DLT_RAW +static int +handle_raw(const u_char * pkt, int len) +{ + return handle_ip((struct ip *) pkt, len); +} + +#endif + +static int +handle_ether(const u_char * pkt, int len) +{ + char buf[PCAP_SNAPLEN]; + struct ether_header *e = (void *) pkt; + unsigned short etype = ntohs(e->ether_type); + if (len < ETHER_HDR_LEN) + return 0; + pkt += ETHER_HDR_LEN; + len -= ETHER_HDR_LEN; + if (ETHERTYPE_8021Q == etype) { + etype = ntohs(*(unsigned short *) (pkt + 2)); + pkt += 4; + len -= 4; + } + if ((ETHERTYPE_IP != etype) + && (ETHERTYPE_IPV6 != etype)) + return 0; + memcpy(buf, pkt, len); + if (ETHERTYPE_IPV6 == etype) + return (handle_ipv6 ((struct ip6_hdr *) buf, len)); + else + return handle_ip((struct ip *) buf, len); +} + +/* public function */ +void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt) +{ + int status; + + fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n", + (void *) udata, (void *) hdr, (void *) pkt, + hdr->caplen); + + if (hdr->caplen < ETHER_HDR_LEN) + return; + + switch (pcap_datalink (pcap_obj)) + { + case DLT_EN10MB: + status = handle_ether (pkt, hdr->caplen); + break; + case DLT_PPP: + status = handle_ppp (pkt, hdr->caplen); + break; +#ifdef DLT_LOOP + case DLT_LOOP: + status = handle_loop (pkt, hdr->caplen); + break; +#endif +#ifdef DLT_RAW + case DLT_RAW: + status = handle_raw (pkt, hdr->caplen); + break; +#endif + case DLT_NULL: + status = handle_null (pkt, hdr->caplen); + break; + + default: + fprintf (stderr, "unsupported data link type %d\n", + pcap_datalink(pcap_obj)); + status = 0; + break; + } /* switch (pcap_datalink(pcap_obj)) */ + + if (0 == status) + return; + + query_count_intvl++; + query_count_total++; + last_ts = hdr->ts; +} + +const char *qtype_str(int t) +{ + static char buf[32]; + switch (t) { + case ns_t_a: return ("A"); + case ns_t_ns: return ("NS"); + case ns_t_md: return ("MD"); + case ns_t_mf: return ("MF"); + case ns_t_cname: return ("CNAME"); + case ns_t_soa: return ("SOA"); + case ns_t_mb: return ("MB"); + case ns_t_mg: return ("MG"); + case ns_t_mr: return ("MR"); + case ns_t_null: return ("NULL"); + case ns_t_wks: return ("WKS"); + case ns_t_ptr: return ("PTR"); + case ns_t_hinfo: return ("HINFO"); + case ns_t_minfo: return ("MINFO"); + case ns_t_mx: return ("MX"); + case ns_t_txt: return ("TXT"); + case ns_t_rp: return ("RP"); + case ns_t_afsdb: return ("AFSDB"); + case ns_t_x25: return ("X25"); + case ns_t_isdn: return ("ISDN"); + case ns_t_rt: return ("RT"); + case ns_t_nsap: return ("NSAP"); + case ns_t_nsap_ptr: return ("NSAP-PTR"); + case ns_t_sig: return ("SIG"); + case ns_t_key: return ("KEY"); + case ns_t_px: return ("PX"); + case ns_t_gpos: return ("GPOS"); + case ns_t_aaaa: return ("AAAA"); + case ns_t_loc: return ("LOC"); + case ns_t_nxt: return ("NXT"); + case ns_t_eid: return ("EID"); + case ns_t_nimloc: return ("NIMLOC"); + case ns_t_srv: return ("SRV"); + case ns_t_atma: return ("ATMA"); + case ns_t_naptr: return ("NAPTR"); + case ns_t_kx: return ("KX"); + case ns_t_cert: return ("CERT"); + case ns_t_a6: return ("A6"); + case ns_t_dname: return ("DNAME"); + case ns_t_sink: return ("SINK"); + case ns_t_opt: return ("OPT"); + case ns_t_tsig: return ("TSIG"); + case ns_t_ixfr: return ("IXFR"); + case ns_t_axfr: return ("AXFR"); + case ns_t_mailb: return ("MAILB"); + case ns_t_maila: return ("MAILA"); + case ns_t_any: return ("ANY"); + case ns_t_zxfr: return ("ZXFR"); + default: + snprintf (buf, 32, "#%i", t); + buf[31] = '\0'; + return (buf); + }; /* switch (t) */ + /* NOTREACHED */ + return (NULL); +} + +const char *opcode_str (int o) +{ + static char buf[30]; + switch (o) { + case 0: + return "Query"; + break; + case 1: + return "Iquery"; + break; + case 2: + return "Status"; + break; + case 4: + return "Notify"; + break; + case 5: + return "Update"; + break; + default: + snprintf(buf, 30, "Opcode%d", o); + return buf; + } + /* NOTREACHED */ +} + +const char *rcode_str (int rcode) +{ + static char buf[32]; + switch (rcode) + { + case ns_r_noerror: return ("NOERROR"); + case ns_r_formerr: return ("FORMERR"); + case ns_r_servfail: return ("SERVFAIL"); + case ns_r_nxdomain: return ("NXDOMAIN"); + case ns_r_notimpl: return ("NOTIMPL"); + case ns_r_refused: return ("REFUSED"); + case ns_r_yxdomain: return ("YXDOMAIN"); + case ns_r_yxrrset: return ("YXRRSET"); + case ns_r_nxrrset: return ("NXRRSET"); + case ns_r_notauth: return ("NOTAUTH"); + case ns_r_notzone: return ("NOTZONE"); + case ns_r_max: return ("MAX"); + case ns_r_badsig: return ("BADSIG"); + case ns_r_badkey: return ("BADKEY"); + case ns_r_badtime: return ("BADTIME"); + default: + snprintf (buf, 32, "RCode%i", rcode); + buf[31] = '\0'; + return (buf); + } + /* Never reached */ + return (NULL); +} /* const char *rcode_str (int rcode) */ + +#if 0 +static int +main(int argc, char *argv[]) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + int x; + struct stat sb; + int readfile_state = 0; + struct bpf_program fp; + + port53 = htons(53); + SubReport = Sources_report; + ignore_addr.s_addr = 0; + progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]); + srandom(time(NULL)); + ResetCounters(); + + while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) { + switch (x) { + case 'a': + anon_flag = 1; + break; + case 's': + sld_flag = 1; + break; + case 't': + nld_flag = 1; + break; + case 'p': + promisc_flag = 0; + break; + case 'b': + bpf_program_str = strdup(optarg); + break; + case 'i': + ignore_addr.s_addr = inet_addr(optarg); + break; + case 'f': + set_filter(optarg); + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + device = strdup(argv[0]); + + if (0 == stat(device, &sb)) + readfile_state = 1; + if (readfile_state) { + pcap_obj = pcap_open_offline(device, errbuf); + } else { + pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf); + } + if (NULL == pcap_obj) { + fprintf(stderr, "pcap_open_*: %s\n", errbuf); + exit(1); + } + + if (0 == isatty(1)) { + if (0 == readfile_state) { + fprintf(stderr, "Non-interactive mode requires savefile argument\n"); + exit(1); + } + interactive = 0; + print_func = printf; + } + + memset(&fp, '\0', sizeof(fp)); + x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0); + if (x < 0) { + fprintf(stderr, "pcap_compile failed\n"); + exit(1); + } + x = pcap_setfilter(pcap_obj, &fp); + if (x < 0) { + fprintf(stderr, "pcap_setfilter failed\n"); + exit(1); + } + + /* + * non-blocking call added for Mac OS X bugfix. Sent by Max Horn. + * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html + */ + x = pcap_setnonblock(pcap_obj, 1, errbuf); + if (x < 0) { + fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf); + exit(1); + } + + switch (pcap_datalink(pcap_obj)) { + case DLT_EN10MB: + handle_datalink = handle_ether; + break; +#if USE_PPP + case DLT_PPP: + handle_datalink = handle_ppp; + break; +#endif +#ifdef DLT_LOOP + case DLT_LOOP: + handle_datalink = handle_loop; + break; +#endif +#ifdef DLT_RAW + case DLT_RAW: + handle_datalink = handle_raw; + break; +#endif + case DLT_NULL: + handle_datalink = handle_null; + break; + default: + fprintf(stderr, "unsupported data link type %d\n", + pcap_datalink(pcap_obj)); + return 1; + break; + } + if (interactive) { + init_curses(); + while (0 == Quit) { + if (readfile_state < 2) { + /* + * On some OSes select() might return 0 even when + * there are packets to process. Thus, we always + * ignore its return value and just call pcap_dispatch() + * anyway. + */ + if (0 == readfile_state) /* interactive */ + pcap_select(pcap_obj, 1, 0); + x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL); + } + if (0 == x && 1 == readfile_state) { + /* block on keyboard until user quits */ + readfile_state++; + nodelay(w, 0); + } + keyboard(); + cron_pre(); + report(); + cron_post(); + } + endwin(); /* klin, Thu Nov 28 08:56:51 2002 */ + } else { + while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL)) + (void) 0; + cron_pre(); + Sources_report(); print_func("\n"); + Destinatioreport(); print_func("\n"); + Qtypes_report(); print_func("\n"); + Opcodes_report(); print_func("\n"); + Tld_report(); print_func("\n"); + Sld_report(); print_func("\n"); + Nld_report(); print_func("\n"); + SldBySource_report(); + } + + pcap_close(pcap_obj); + return 0; +} /* static int main(int argc, char *argv[]) */ +#endif diff --git a/src/utils_dns.h b/src/utils_dns.h new file mode 100644 index 00000000..6a613a78 --- /dev/null +++ b/src/utils_dns.h @@ -0,0 +1,81 @@ +#ifndef COLLECTD_UTILS_DNS_H +#define COLLECTD_UTILS_DNS_H 1 +/* + * collectd - src/utils_dns.h + * Copyright (C) 2006 Florian octo Forster + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of The Measurement Factory nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * Florian octo Forster + */ + +#include +#include + +#define T_MAX 65536 +#define OP_MAX 16 +#define C_MAX 65536 +#define MAX_QNAME_SZ 512 + +struct rfc1035_header_s { + uint16_t id; + unsigned int qr:1; + unsigned int opcode:4; + unsigned int aa:1; + unsigned int tc:1; + unsigned int rd:1; + unsigned int ra:1; + unsigned int z:1; + unsigned int ad:1; + unsigned int cd:1; + unsigned int rcode:4; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; + uint16_t qtype; + uint16_t qclass; + char qname[MAX_QNAME_SZ]; + uint16_t length; +}; +typedef struct rfc1035_header_s rfc1035_header_t; + +extern int qtype_counts[T_MAX]; +extern int opcode_counts[OP_MAX]; +extern int qclass_counts[C_MAX]; + +void dnstop_set_pcap_obj (pcap_t *po); +void dnstop_set_callback (void (*cb) (const rfc1035_header_t *)); + +void ignore_list_add_name (const char *name); +void handle_pcap (u_char * udata, const struct pcap_pkthdr *hdr, const u_char * pkt); + +const char *qtype_str(int t); +const char *opcode_str(int o); +const char *rcode_str (int r); + +#endif /* !COLLECTD_UTILS_DNS_H */