AC_SUBST(LCC_VERSION_EXTRA)
AC_SUBST(LCC_VERSION_STRING)
-AC_CONFIG_FILES(src/libcollectdclient/lcc_features.h)
+AC_CONFIG_FILES(src/libcollectdclient/collectd/lcc_features.h)
AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/owniptc/Makefile src/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile)
AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"'
sbin_PROGRAMS = collectd collectdmon
-bin_PROGRAMS = collectd-nagios collectdctl
+bin_PROGRAMS = collectd-nagios collectdctl collectd-tg
collectd_SOURCES = collectd.c collectd.h \
common.c common.h \
collectdctl_LDADD += libcollectdclient/libcollectdclient.la
collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la
+collectd_tg_SOURCES = collectd-tg.c \
+ utils_heap.c utils_heap.h
+collectd_tg_LDADD =
+if BUILD_WITH_LIBSOCKET
+collectd_tg_LDADD += -lsocket
+endif
+if BUILD_AIX
+collectd_tg_LDADD += -lm
+endif
+collectd_tg_LDADD += libcollectdclient/libcollectdclient.la
+collectd_tg_DEPENDENCIES = libcollectdclient/libcollectdclient.la
+
pkglib_LTLIBRARIES =
#include <assert.h>
#include <math.h>
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
#define RET_OKAY 0
#define RET_WARNING 1
--- /dev/null
+/**
+ * collectd-td - collectd traffic generator
+ * Copyright (C) 2010 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; only version 2 of the License is applicable.
+ *
+ * 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 Forster <ff at octo.it>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200809L
+#endif
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 700
+#endif
+
+#if !__GNUC__
+# define __attribute__(x) /**/
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "utils_heap.h"
+
+#include "libcollectdclient/collectd/client.h"
+#include "libcollectdclient/collectd/network.h"
+#include "libcollectdclient/collectd/network_buffer.h"
+
+#define DEF_NUM_HOSTS 1000
+#define DEF_NUM_PLUGINS 20
+#define DEF_NUM_VALUES 100000
+#define DEF_INTERVAL 10
+
+static int conf_num_hosts = DEF_NUM_HOSTS;
+static int conf_num_plugins = DEF_NUM_PLUGINS;
+static int conf_num_values = DEF_NUM_VALUES;
+static int conf_interval = DEF_INTERVAL;
+static const char *conf_destination = NET_DEFAULT_V6_ADDR;
+static const char *conf_service = NET_DEFAULT_PORT;
+
+static lcc_network_t *net;
+
+static c_heap_t *values_heap = NULL;
+
+static struct sigaction sigint_action;
+static struct sigaction sigterm_action;
+
+static _Bool loop = 1;
+
+__attribute__((noreturn))
+static void exit_usage (int exit_status) /* {{{ */
+{
+ fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
+ "collectd-tg -- collectd traffic generator\n"
+ "\n"
+ " Usage: collectd-ng [OPTION]\n"
+ "\n"
+ " Valid options:\n"
+ " -n <number> Number of value lists. (Default: %i)\n"
+ " -H <number> Number of hosts to emulate. (Default: %i)\n"
+ " -p <number> Number of plugins to emulate. (Default: %i)\n"
+ " -i <seconds> Interval of each value in seconds. (Default: %i)\n"
+ " -d <dest> Destination address of the network packets.\n"
+ " (Default: %s)\n"
+ " -D <port> Destination port of the network packets.\n"
+ " (Default: %s)\n"
+ " -h Print usage information (this output).\n"
+ "\n"
+ "Copyright (C) 2010 Florian Forster\n"
+ "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
+ DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS,
+ DEF_INTERVAL,
+ NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT);
+ exit (exit_status);
+} /* }}} void exit_usage */
+
+static void signal_handler (int signal) /* {{{ */
+{
+ loop = 0;
+} /* }}} void signal_handler */
+
+static int compare_time (const void *v0, const void *v1) /* {{{ */
+{
+ const lcc_value_list_t *vl0 = v0;
+ const lcc_value_list_t *vl1 = v1;
+
+ if (vl0->time < vl1->time)
+ return (-1);
+ else if (vl0->time > vl1->time)
+ return (1);
+ else
+ return (0);
+} /* }}} int compare_time */
+
+static int get_boundet_random (int min, int max) /* {{{ */
+{
+ int range;
+
+ if (min >= max)
+ return (-1);
+ if (min == (max - 1))
+ return (min);
+
+ range = max - min;
+
+ return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0))));
+} /* }}} int get_boundet_random */
+
+static lcc_value_list_t *create_value_list (void) /* {{{ */
+{
+ lcc_value_list_t *vl;
+ int host_num;
+
+ vl = malloc (sizeof (*vl));
+ if (vl == NULL)
+ {
+ fprintf (stderr, "malloc failed.\n");
+ return (NULL);
+ }
+ memset (vl, 0, sizeof (*vl));
+
+ vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
+ if (vl->values == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ free (vl);
+ return (NULL);
+ }
+
+ vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
+ if (vl->values_types == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ free (vl->values);
+ free (vl);
+ return (NULL);
+ }
+
+ vl->values_len = 1;
+
+ host_num = get_boundet_random (0, conf_num_hosts);
+
+ vl->interval = conf_interval;
+ vl->time = time (NULL) + (host_num % vl->interval) + 1;
+
+ if (get_boundet_random (0, 2) == 0)
+ vl->values_types[0] = LCC_TYPE_GAUGE;
+ else
+ vl->values_types[0] = LCC_TYPE_DERIVE;
+
+ snprintf (vl->identifier.host, sizeof (vl->identifier.host),
+ "host%04i", host_num);
+ snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
+ "plugin%03i", get_boundet_random (0, conf_num_plugins));
+ strncpy (vl->identifier.type,
+ (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
+ sizeof (vl->identifier.type));
+ snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
+ "ti%li", random ());
+
+ return (vl);
+} /* }}} int create_value_list */
+
+static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
+{
+ if (vl == NULL)
+ return;
+
+ free (vl->values);
+ free (vl->values_types);
+ free (vl);
+} /* }}} void destroy_value_list */
+
+static int send_value (lcc_value_list_t *vl) /* {{{ */
+{
+ int status;
+
+ if (vl->values_types[0] == LCC_TYPE_GAUGE)
+ vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
+ else
+ vl->values[0].derive += get_boundet_random (0, 100);
+
+ status = lcc_network_values_send (net, vl);
+ if (status != 0)
+ fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
+
+ vl->time += vl->interval;
+
+ return (0);
+} /* }}} int send_value */
+
+static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
+{
+ char *endptr;
+ int tmp;
+
+ errno = 0;
+ endptr = NULL;
+ tmp = (int) strtol (str, &endptr, /* base = */ 0);
+ if (errno != 0)
+ {
+ fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
+ str, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ else if (endptr == str)
+ {
+ fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
+ exit (EXIT_FAILURE);
+ }
+ else if (*endptr != 0)
+ {
+ fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
+ exit (EXIT_FAILURE);
+ }
+
+ *ret_value = tmp;
+ return (0);
+} /* }}} int get_integer_opt */
+
+static int read_options (int argc, char **argv) /* {{{ */
+{
+ int opt;
+
+ while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1)
+ {
+ switch (opt)
+ {
+ case 'n':
+ get_integer_opt (optarg, &conf_num_values);
+ break;
+
+ case 'H':
+ get_integer_opt (optarg, &conf_num_hosts);
+ break;
+
+ case 'p':
+ get_integer_opt (optarg, &conf_num_plugins);
+ break;
+
+ case 'i':
+ get_integer_opt (optarg, &conf_interval);
+ break;
+
+ case 'd':
+ conf_destination = optarg;
+ break;
+
+ case 'D':
+ conf_service = optarg;
+ break;
+
+ case 'h':
+ exit_usage (EXIT_SUCCESS);
+
+ default:
+ exit_usage (EXIT_FAILURE);
+ } /* switch (opt) */
+ } /* while (getopt) */
+
+ return (0);
+} /* }}} int read_options */
+
+int main (int argc, char **argv) /* {{{ */
+{
+ int i;
+ time_t last_time;
+ int values_sent = 0;
+
+ read_options (argc, argv);
+
+ sigint_action.sa_handler = signal_handler;
+ sigaction (SIGINT, &sigint_action, /* old = */ NULL);
+
+ sigterm_action.sa_handler = signal_handler;
+ sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
+
+
+ values_heap = c_heap_create (compare_time);
+ if (values_heap == NULL)
+ {
+ fprintf (stderr, "c_heap_create failed.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ net = lcc_network_create ();
+ if (net == NULL)
+ {
+ fprintf (stderr, "lcc_network_create failed.\n");
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ lcc_server_t *srv;
+
+ srv = lcc_server_create (net, conf_destination, conf_service);
+ if (srv == NULL)
+ {
+ fprintf (stderr, "lcc_server_create failed.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ lcc_server_set_ttl (srv, 42);
+#if 0
+ lcc_server_set_security_level (srv, ENCRYPT,
+ "admin", "password1");
+#endif
+ }
+
+ fprintf (stdout, "Creating %i values ... ", conf_num_values);
+ fflush (stdout);
+ for (i = 0; i < conf_num_values; i++)
+ {
+ lcc_value_list_t *vl;
+
+ vl = create_value_list ();
+ if (vl == NULL)
+ {
+ fprintf (stderr, "create_value_list failed.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ c_heap_insert (values_heap, vl);
+ }
+ fprintf (stdout, "done\n");
+
+ last_time = 0;
+ while (loop)
+ {
+ lcc_value_list_t *vl = c_heap_get_root (values_heap);
+
+ if (vl == NULL)
+ break;
+
+ if (vl->time != last_time)
+ {
+ printf ("%i values have been sent.\n", values_sent);
+
+ /* Check if we need to sleep */
+ time_t now = time (NULL);
+
+ while (now < vl->time)
+ {
+ /* 1 / 100 second */
+ struct timespec ts = { 0, 10000000 };
+ nanosleep (&ts, /* remaining = */ NULL);
+ now = time (NULL);
+
+ if (!loop)
+ break;
+ }
+ last_time = vl->time;
+ }
+
+ send_value (vl);
+ values_sent++;
+
+ c_heap_insert (values_heap, vl);
+ }
+
+ fprintf (stdout, "Shutting down.\n");
+ fflush (stdout);
+
+ while (42)
+ {
+ lcc_value_list_t *vl = c_heap_get_root (values_heap);
+ if (vl == NULL)
+ break;
+ destroy_value_list (vl);
+ }
+ c_heap_destroy (values_heap);
+
+ lcc_network_destroy (net);
+ exit (EXIT_SUCCESS);
+ return (0);
+} /* }}} int main */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
#include <errno.h>
#include <math.h>
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
#define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
AM_CFLAGS = -Wall -Werror
endif
-pkginclude_HEADERS = client.h lcc_features.h
+pkginclude_HEADERS = collectd/client.h collectd/network_buffer.h collectd/lcc_features.h
lib_LTLIBRARIES = libcollectdclient.la
nodist_pkgconfig_DATA = libcollectdclient.pc
-BUILT_SOURCES = lcc_features.h
+BUILT_SOURCES = collectd/lcc_features.h
-libcollectdclient_la_SOURCES = client.c
+libcollectdclient_la_SOURCES = client.c network.c network_buffer.c
+libcollectdclient_la_CPPFLAGS = $(AM_CPPFLAGS)
libcollectdclient_la_LDFLAGS = -version-info 0:0:0
+libcollectdclient_la_LIBADD =
+if BUILD_WITH_LIBGCRYPT
+libcollectdclient_la_CPPFLAGS += $(GCRYPT_CPPFLAGS)
+libcollectdclient_la_LDFLAGS += $(GCRYPT_LDFLAGS)
+libcollectdclient_la_LIBADD += $(GCRYPT_LIBS)
+endif
# define __attribute__(x) /**/
#endif
-#include "lcc_features.h"
+#include "collectd/lcc_features.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <netdb.h>
-#include "client.h"
+#include "collectd/client.h"
/* NI_MAXHOST has been obsoleted by RFC 3493 which is a reason for SunOS 5.11
* to no longer define it. We'll use the old, RFC 2553 value here. */
return (0);
} /* }}} int lcc_string_to_identifier */
+int lcc_identifier_compare (const lcc_identifier_t *i0, /* {{{ */
+ const lcc_identifier_t *i1)
+{
+ int status;
+
+ if ((i0 == NULL) && (i1 == NULL))
+ return (0);
+ else if (i0 == NULL)
+ return (-1);
+ else if (i1 == NULL)
+ return (1);
+
+#define CMP_FIELD(f) do { \
+ status = strcmp (i0->f, i1->f); \
+ if (status != 0) \
+ return (status); \
+} while (0);
+
+ CMP_FIELD (host);
+ CMP_FIELD (plugin);
+ CMP_FIELD (plugin_instance);
+ CMP_FIELD (type);
+ CMP_FIELD (type_instance);
+
+#undef CMP_FIELD
+
+ return (0);
+} /* }}} int lcc_identifier_compare */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
+++ /dev/null
-/**
- * libcollectdclient - src/libcollectdclient/client.h
- * Copyright (C) 2008 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; only version 2 of the License is applicable.
- *
- * 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 <octo at verplant.org>
- **/
-
-#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
-#define LIBCOLLECTD_COLLECTDCLIENT_H 1
-
-#include "lcc_features.h"
-
-/*
- * Includes (for data types)
- */
-#if HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#include <inttypes.h>
-#include <time.h>
-
-/*
- * Defines
- */
-#define LCC_NAME_LEN 64
-#define LCC_DEFAULT_PORT "25826"
-
-/*
- * Types
- */
-#define LCC_TYPE_COUNTER 0
-#define LCC_TYPE_GAUGE 1
-#define LCC_TYPE_DERIVE 2
-#define LCC_TYPE_ABSOLUTE 3
-
-LCC_BEGIN_DECLS
-
-typedef uint64_t counter_t;
-typedef double gauge_t;
-typedef uint64_t derive_t;
-typedef uint64_t absolute_t;
-
-union value_u
-{
- counter_t counter;
- gauge_t gauge;
- derive_t derive;
- absolute_t absolute;
-};
-typedef union value_u value_t;
-
-struct lcc_identifier_s
-{
- char host[LCC_NAME_LEN];
- char plugin[LCC_NAME_LEN];
- char plugin_instance[LCC_NAME_LEN];
- char type[LCC_NAME_LEN];
- char type_instance[LCC_NAME_LEN];
-};
-typedef struct lcc_identifier_s lcc_identifier_t;
-#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
-
-struct lcc_value_list_s
-{
- value_t *values;
- int *values_types;
- size_t values_len;
- time_t time;
- int interval;
- lcc_identifier_t identifier;
-};
-typedef struct lcc_value_list_s lcc_value_list_t;
-#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
-
-struct lcc_connection_s;
-typedef struct lcc_connection_s lcc_connection_t;
-
-/*
- * Functions
- */
-int lcc_connect (const char *address, lcc_connection_t **ret_con);
-int lcc_disconnect (lcc_connection_t *c);
-#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
-
-int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
- size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
-
-int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
-
-int lcc_flush (lcc_connection_t *c, const char *plugin,
- lcc_identifier_t *ident, int timeout);
-
-int lcc_listval (lcc_connection_t *c,
- lcc_identifier_t **ret_ident, size_t *ret_ident_num);
-
-/* TODO: putnotif */
-
-const char *lcc_strerror (lcc_connection_t *c);
-
-int lcc_identifier_to_string (lcc_connection_t *c,
- char *string, size_t string_size, const lcc_identifier_t *ident);
-int lcc_string_to_identifier (lcc_connection_t *c,
- lcc_identifier_t *ident, const char *string);
-
-LCC_END_DECLS
-
-/* vim: set sw=2 sts=2 et : */
-#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
--- /dev/null
+/**
+ * libcollectdclient - src/libcollectdclient/client.h
+ * Copyright (C) 2008 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; only version 2 of the License is applicable.
+ *
+ * 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 <octo at verplant.org>
+ **/
+
+#ifndef LIBCOLLECTD_COLLECTDCLIENT_H
+#define LIBCOLLECTD_COLLECTDCLIENT_H 1
+
+#include "lcc_features.h"
+
+/*
+ * Includes (for data types)
+ */
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <inttypes.h>
+#include <time.h>
+
+/*
+ * Defines
+ */
+#define LCC_NAME_LEN 64
+#define LCC_DEFAULT_PORT "25826"
+
+/*
+ * Types
+ */
+#define LCC_TYPE_COUNTER 0
+#define LCC_TYPE_GAUGE 1
+#define LCC_TYPE_DERIVE 2
+#define LCC_TYPE_ABSOLUTE 3
+
+LCC_BEGIN_DECLS
+
+typedef uint64_t counter_t;
+typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
+
+union value_u
+{
+ counter_t counter;
+ gauge_t gauge;
+ derive_t derive;
+ absolute_t absolute;
+};
+typedef union value_u value_t;
+
+struct lcc_identifier_s
+{
+ char host[LCC_NAME_LEN];
+ char plugin[LCC_NAME_LEN];
+ char plugin_instance[LCC_NAME_LEN];
+ char type[LCC_NAME_LEN];
+ char type_instance[LCC_NAME_LEN];
+};
+typedef struct lcc_identifier_s lcc_identifier_t;
+#define LCC_IDENTIFIER_INIT { "localhost", "", "", "", "" }
+
+struct lcc_value_list_s
+{
+ value_t *values;
+ int *values_types;
+ size_t values_len;
+ time_t time;
+ int interval;
+ lcc_identifier_t identifier;
+};
+typedef struct lcc_value_list_s lcc_value_list_t;
+#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT }
+
+struct lcc_connection_s;
+typedef struct lcc_connection_s lcc_connection_t;
+
+/*
+ * Functions
+ */
+int lcc_connect (const char *address, lcc_connection_t **ret_con);
+int lcc_disconnect (lcc_connection_t *c);
+#define LCC_DESTROY(c) do { lcc_disconnect (c); (c) = NULL; } while (0)
+
+int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident,
+ size_t *ret_values_num, gauge_t **ret_values, char ***ret_values_names);
+
+int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl);
+
+int lcc_flush (lcc_connection_t *c, const char *plugin,
+ lcc_identifier_t *ident, int timeout);
+
+int lcc_listval (lcc_connection_t *c,
+ lcc_identifier_t **ret_ident, size_t *ret_ident_num);
+
+/* TODO: putnotif */
+
+const char *lcc_strerror (lcc_connection_t *c);
+
+int lcc_identifier_to_string (lcc_connection_t *c,
+ char *string, size_t string_size, const lcc_identifier_t *ident);
+int lcc_string_to_identifier (lcc_connection_t *c,
+ lcc_identifier_t *ident, const char *string);
+
+/* Compares the identifiers "i0" and "i1" and returns less than zero or greater
+ * than zero if "i0" is smaller than or greater than "i1", respectively. If
+ * "i0" and "i1" are identical, zero is returned. */
+int lcc_identifier_compare (const lcc_identifier_t *i0,
+ const lcc_identifier_t *i1);
+
+LCC_END_DECLS
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
--- /dev/null
+/**
+ * libcollectdclient - src/libcollectdclient/lcc_features.h
+ * Copyright (C) 2009 Sebastian Harl
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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:
+ * Sebastian tokkee Harl <sh at tokkee.org>
+ **/
+
+#ifndef LIBCOLLECTD_LCC_FEATURES_H
+#define LIBCOLLECTD_LCC_FEATURES_H 1
+
+#ifdef __cplusplus
+# define LCC_BEGIN_DECLS extern "C" {
+# define LCC_END_DECLS }
+#else
+# define LCC_BEGIN_DECLS
+# define LCC_END_DECLS
+#endif
+
+#define LCC_API_VERSION 0
+
+#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
+#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
+#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
+
+#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
+
+#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
+
+#define LCC_VERSION_ENCODE(major, minor, patch) \
+ ((major) * 10000 + (minor) * 100 + (patch))
+
+#define LCC_VERSION \
+ LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
+
+LCC_BEGIN_DECLS
+
+unsigned int lcc_version (void);
+
+const char *lcc_version_string (void);
+
+const char *lcc_version_extra (void);
+
+LCC_END_DECLS
+
+#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
--- /dev/null
+/**
+ * collectd - src/libcollectdclient/network.h
+ * Copyright (C) 2005-2010 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 of the License is
+ * applicable.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <octo at verplant.org>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_H
+#define LIBCOLLECTDCLIENT_NETWORK_H 1
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "client.h"
+
+#define NET_DEFAULT_V4_ADDR "239.192.74.66"
+#define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
+#define NET_DEFAULT_PORT "25826"
+
+struct lcc_network_s;
+typedef struct lcc_network_s lcc_network_t;
+
+struct lcc_server_s;
+typedef struct lcc_server_s lcc_server_t;
+
+enum lcc_security_level_e
+{
+ NONE,
+ SIGN,
+ ENCRYPT
+};
+typedef enum lcc_security_level_e lcc_security_level_t;
+
+/*
+ * Create / destroy object
+ */
+lcc_network_t *lcc_network_create (void);
+void lcc_network_destroy (lcc_network_t *net);
+
+/*
+ * Add servers
+ */
+lcc_server_t *lcc_server_create (lcc_network_t *net,
+ const char *node, const char *service);
+int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv);
+
+/* Configure servers */
+int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl);
+int lcc_server_set_security_level (lcc_server_t *srv,
+ lcc_security_level_t level,
+ const char *username, const char *password);
+
+/*
+ * Send data
+ */
+int lcc_network_values_send (lcc_network_t *net,
+ const lcc_value_list_t *vl);
+#if 0
+int lcc_network_notification_send (lcc_network_t *net,
+ const lcc_notification_t *notif);
+#endif
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTDCLIENT_NETWORK_H */
--- /dev/null
+/**
+ * collectd - src/libcollectdclient/network_buffer.h
+ * Copyright (C) 2010 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 of the License is
+ * applicable.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <octo at verplant.org>
+ **/
+
+#ifndef LIBCOLLECTDCLIENT_NETWORK_BUFFER_H
+#define LIBCOLLECTDCLIENT_NETWORK_BUFFER_H 1
+
+/* FIXME */
+#include "client.h"
+#include "network.h"
+
+/* Ethernet frame - (IPv6 header + UDP header) */
+#define LCC_NETWORK_BUFFER_SIZE_DEFAULT 1452
+
+struct lcc_network_buffer_s;
+typedef struct lcc_network_buffer_s lcc_network_buffer_t;
+
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size);
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb,
+ lcc_security_level_t level,
+ const char *user, const char *password);
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb);
+int lcc_network_buffer_finalize (lcc_network_buffer_t *nb);
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb,
+ const lcc_value_list_t *vl);
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb,
+ void *buffer, size_t *buffer_size);
+
+#endif /* LIBCOLLECTDCLIENT_NETWORK_BUFFER_H */
+/* vim: set sw=2 sts=2 et : */
+++ /dev/null
-/**
- * libcollectdclient - src/libcollectdclient/lcc_features.h
- * Copyright (C) 2009 Sebastian Harl
- *
- * 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; only version 2 of the License is applicable.
- *
- * 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:
- * Sebastian tokkee Harl <sh at tokkee.org>
- **/
-
-#ifndef LIBCOLLECTD_LCC_FEATURES_H
-#define LIBCOLLECTD_LCC_FEATURES_H 1
-
-#ifdef __cplusplus
-# define LCC_BEGIN_DECLS extern "C" {
-# define LCC_END_DECLS }
-#else
-# define LCC_BEGIN_DECLS
-# define LCC_END_DECLS
-#endif
-
-#define LCC_API_VERSION 0
-
-#define LCC_VERSION_MAJOR @LCC_VERSION_MAJOR@
-#define LCC_VERSION_MINOR @LCC_VERSION_MINOR@
-#define LCC_VERSION_PATCH @LCC_VERSION_PATCH@
-
-#define LCC_VERSION_EXTRA "@LCC_VERSION_EXTRA@"
-
-#define LCC_VERSION_STRING "@LCC_VERSION_STRING@"
-
-#define LCC_VERSION_ENCODE(major, minor, patch) \
- ((major) * 10000 + (minor) * 100 + (patch))
-
-#define LCC_VERSION \
- LCC_VERSION_ENCODE(LCC_VERSION_MAJOR, LCC_VERSION_MINOR, LCC_VERSION_PATCH)
-
-LCC_BEGIN_DECLS
-
-unsigned int lcc_version (void);
-
-const char *lcc_version_string (void);
-
-const char *lcc_version_extra (void);
-
-LCC_END_DECLS
-
-#endif /* ! LIBCOLLECTD_LCC_FEATURES_H */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
--- /dev/null
+/**
+ * collectd - src/libcollectdclient/network.h
+ * Copyright (C) 2005-2010 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 of the License is
+ * applicable.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <octo at verplant.org>
+ **/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "collectd/network.h"
+#include "collectd/network_buffer.h"
+
+/*
+ * Private data types
+ */
+struct lcc_network_s
+{
+ lcc_server_t *servers;
+};
+
+struct lcc_server_s
+{
+ char *node;
+ char *service;
+
+ int ttl;
+ lcc_security_level_t security_level;
+ char *username;
+ char *password;
+
+ int fd;
+ struct sockaddr *sa;
+ socklen_t sa_len;
+
+ lcc_network_buffer_t *buffer;
+
+ lcc_server_t *next;
+};
+
+/*
+ * Private functions
+ */
+static int server_close_socket (lcc_server_t *srv) /* {{{ */
+{
+ if (srv == NULL)
+ return (EINVAL);
+
+ if (srv->fd < 0)
+ return (0);
+
+ close (srv->fd);
+ free (srv->sa);
+ srv->sa = NULL;
+ srv->sa_len = 0;
+
+ return (0);
+} /* }}} int server_close_socket */
+
+static void int_server_destroy (lcc_server_t *srv) /* {{{ */
+{
+ lcc_server_t *next;
+
+ if (srv == NULL)
+ return;
+
+ server_close_socket (srv);
+
+ next = srv->next;
+
+ if (srv->fd >= 0)
+ {
+ close (srv->fd);
+ srv->fd = -1;
+ }
+
+ free (srv->node);
+ free (srv->service);
+ free (srv->username);
+ free (srv->password);
+ free (srv);
+
+ int_server_destroy (next);
+} /* }}} void int_server_destroy */
+
+static int server_open_socket (lcc_server_t *srv) /* {{{ */
+{
+ struct addrinfo ai_hints = { 0 };
+ struct addrinfo *ai_list = NULL;
+ struct addrinfo *ai_ptr;
+ int status;
+
+ if (srv == NULL)
+ return (EINVAL);
+
+ if (srv->fd >= 0)
+ server_close_socket (srv);
+
+#ifdef AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_socktype = SOCK_DGRAM;
+
+ status = getaddrinfo (srv->node, srv->service, &ai_hints, &ai_list);
+ if (status != 0)
+ return (status);
+ assert (ai_list != NULL);
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ srv->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+ if (srv->fd < 0)
+ continue;
+
+ if (ai_ptr->ai_family == AF_INET)
+ {
+
+ struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
+ int optname;
+
+ if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ optname = IP_MULTICAST_TTL;
+ else
+ optname = IP_TTL;
+
+ setsockopt (srv->fd, IPPROTO_IP, optname,
+ &srv->ttl,
+ sizeof (srv->ttl));
+ }
+ else if (ai_ptr->ai_family == AF_INET6)
+ {
+ /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
+ int optname;
+
+ if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+ optname = IPV6_MULTICAST_HOPS;
+ else
+ optname = IPV6_UNICAST_HOPS;
+
+ setsockopt (srv->fd, IPPROTO_IPV6, optname,
+ &srv->ttl,
+ sizeof (srv->ttl));
+ }
+
+ srv->sa = malloc (ai_ptr->ai_addrlen);
+ if (srv->sa == NULL)
+ {
+ close (srv->fd);
+ srv->fd = -1;
+ continue;
+ }
+
+ memcpy (srv->sa, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ srv->sa_len = ai_ptr->ai_addrlen;
+ break;
+ }
+
+ freeaddrinfo (ai_list);
+
+ if (srv->fd < 0)
+ return (-1);
+ return (0);
+} /* }}} int server_open_socket */
+
+static int server_send_buffer (lcc_server_t *srv) /* {{{ */
+{
+ char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
+ size_t buffer_size;
+ int status;
+
+ if (srv->fd < 0)
+ {
+ status = server_open_socket (srv);
+ if (status != 0)
+ return (status);
+ }
+
+ memset (buffer, 0, sizeof (buffer));
+ buffer_size = sizeof (buffer);
+
+ lcc_network_buffer_finalize (srv->buffer);
+ status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size);
+ lcc_network_buffer_initialize (srv->buffer);
+
+ if (status != 0)
+ return (status);
+
+ if (buffer_size > sizeof (buffer))
+ buffer_size = sizeof (buffer);
+
+ while (42)
+ {
+ assert (srv->fd >= 0);
+ assert (srv->sa != NULL);
+ status = (int) sendto (srv->fd, buffer, buffer_size, /* flags = */ 0,
+ srv->sa, srv->sa_len);
+ if ((status < 0) && ((errno == EINTR) || (errno == EAGAIN)))
+ continue;
+
+ break;
+ }
+
+ if (status < 0)
+ return (status);
+ return (0);
+} /* }}} int server_send_buffer */
+
+static int server_value_add (lcc_server_t *srv, /* {{{ */
+ const lcc_value_list_t *vl)
+{
+ int status;
+
+ status = lcc_network_buffer_add_value (srv->buffer, vl);
+ if (status == 0)
+ return (0);
+
+ server_send_buffer (srv);
+ return (lcc_network_buffer_add_value (srv->buffer, vl));
+} /* }}} int server_value_add */
+
+/*
+ * Public functions
+ */
+lcc_network_t *lcc_network_create (void) /* {{{ */
+{
+ lcc_network_t *net;
+
+ net = malloc (sizeof (*net));
+ if (net == NULL)
+ return (NULL);
+ memset (net, 0, sizeof (*net));
+
+ net->servers = NULL;
+
+ return (net);
+} /* }}} lcc_network_t *lcc_network_create */
+
+void lcc_network_destroy (lcc_network_t *net) /* {{{ */
+{
+ if (net == NULL)
+ return;
+ int_server_destroy (net->servers);
+ free (net);
+} /* }}} void lcc_network_destroy */
+
+lcc_server_t *lcc_server_create (lcc_network_t *net, /* {{{ */
+ const char *node, const char *service)
+{
+ lcc_server_t *srv;
+
+ if ((net == NULL) || (node == NULL))
+ return (NULL);
+ if (service == NULL)
+ service = NET_DEFAULT_PORT;
+
+ srv = malloc (sizeof (*srv));
+ if (srv == NULL)
+ return (NULL);
+ memset (srv, 0, sizeof (*srv));
+
+ srv->fd = -1;
+ srv->security_level = NONE;
+ srv->username = NULL;
+ srv->password = NULL;
+ srv->next = NULL;
+
+ srv->node = strdup (node);
+ if (srv->node == NULL)
+ {
+ free (srv);
+ return (NULL);
+ }
+
+ srv->service = strdup (service);
+ if (srv->service == NULL)
+ {
+ free (srv->node);
+ free (srv);
+ return (NULL);
+ }
+
+ srv->buffer = lcc_network_buffer_create (/* size = */ 0);
+ if (srv->buffer == NULL)
+ {
+ free (srv->service);
+ free (srv->node);
+ free (srv);
+ return (NULL);
+ }
+
+ if (net->servers == NULL)
+ {
+ net->servers = srv;
+ }
+ else
+ {
+ lcc_server_t *last = net->servers;
+
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = srv;
+ }
+
+ return (srv);
+} /* }}} lcc_server_t *lcc_server_create */
+
+int lcc_server_destroy (lcc_network_t *net, lcc_server_t *srv) /* {{{ */
+{
+ if ((net == NULL) || (srv == NULL))
+ return (EINVAL);
+
+ if (net->servers == srv)
+ {
+ net->servers = srv->next;
+ srv->next = NULL;
+ }
+ else
+ {
+ lcc_server_t *prev = net->servers;
+
+ while ((prev != NULL) && (prev->next != srv))
+ prev = prev->next;
+
+ if (prev == NULL)
+ return (ENOENT);
+
+ prev->next = srv->next;
+ srv->next = NULL;
+ }
+
+ int_server_destroy (srv);
+
+ return (0);
+} /* }}} int lcc_server_destroy */
+
+int lcc_server_set_ttl (lcc_server_t *srv, uint8_t ttl) /* {{{ */
+{
+ if (srv == NULL)
+ return (EINVAL);
+
+ srv->ttl = (int) ttl;
+
+ return (0);
+} /* }}} int lcc_server_set_ttl */
+
+int lcc_server_set_security_level (lcc_server_t *srv, /* {{{ */
+ lcc_security_level_t level,
+ const char *username, const char *password)
+{
+ return (lcc_network_buffer_set_security_level (srv->buffer,
+ level, username, password));
+} /* }}} int lcc_server_set_security_level */
+
+int lcc_network_values_send (lcc_network_t *net, /* {{{ */
+ const lcc_value_list_t *vl)
+{
+ lcc_server_t *srv;
+
+ if ((net == NULL) || (vl == NULL))
+ return (EINVAL);
+
+ for (srv = net->servers; srv != NULL; srv = srv->next)
+ server_value_add (srv, vl);
+
+ return (0);
+} /* }}} int lcc_network_values_send */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/**
+ * collectd - src/libcollectdclient/network_buffer.c
+ * Copyright (C) 2010 Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; only version 2.1 of the License is
+ * applicable.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <octo at verplant.org>
+ **/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <errno.h>
+#include <arpa/inet.h> /* htons */
+
+#include <pthread.h>
+
+#if HAVE_LIBGCRYPT
+#include <gcrypt.h>
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+
+#include "collectd/network_buffer.h"
+
+#define TYPE_HOST 0x0000
+#define TYPE_TIME 0x0001
+#define TYPE_PLUGIN 0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE 0x0004
+#define TYPE_TYPE_INSTANCE 0x0005
+#define TYPE_VALUES 0x0006
+#define TYPE_INTERVAL 0x0007
+
+/* Types to transmit notifications */
+#define TYPE_MESSAGE 0x0100
+#define TYPE_SEVERITY 0x0101
+
+#define TYPE_SIGN_SHA256 0x0200
+#define TYPE_ENCR_AES256 0x0210
+
+#define PART_SIGNATURE_SHA256_SIZE 36
+#define PART_ENCRYPTION_AES256_SIZE 42
+
+#define ADD_GENERIC(nb,srcptr,size) do { \
+ assert ((size) <= (nb)->free); \
+ memcpy ((nb)->ptr, (srcptr), (size)); \
+ (nb)->ptr += (size); \
+ (nb)->free -= (size); \
+} while (0)
+
+#define ADD_STATIC(nb,var) \
+ ADD_GENERIC(nb,&(var),sizeof(var));
+
+/*
+ * Data types
+ */
+struct lcc_network_buffer_s
+{
+ char *buffer;
+ size_t size;
+
+ lcc_value_list_t state;
+ char *ptr;
+ size_t free;
+
+ lcc_security_level_t seclevel;
+ char *username;
+ char *password;
+
+ gcry_cipher_hd_t encr_cypher;
+ size_t encr_header_len;
+ char encr_iv[16];
+};
+
+#define SSTRNCPY(dst,src,sz) do { \
+ strncpy ((dst), (src), (sz)); \
+ (dst)[(sz) - 1] = 0; \
+} while (0)
+
+/*
+ * Private functions
+ */
+static _Bool have_gcrypt (void) /* {{{ */
+{
+ static _Bool result = 0;
+ static _Bool need_init = 1;
+
+ if (!need_init)
+ return (result);
+ need_init = 0;
+
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+
+ if (!gcry_check_version (GCRYPT_VERSION))
+ return (0);
+
+ gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ result = 1;
+ return (1);
+} /* }}} _Bool have_gcrypt */
+
+static uint64_t htonll (uint64_t val) /* {{{ */
+{
+ static int config = 0;
+
+ uint32_t hi;
+ uint32_t lo;
+
+ if (config == 0)
+ {
+ uint16_t h = 0x1234;
+ uint16_t n = htons (h);
+
+ if (h == n)
+ config = 1;
+ else
+ config = 2;
+ }
+
+ if (config == 1)
+ return (val);
+
+ hi = (uint32_t) (val >> 32);
+ lo = (uint32_t) (val & 0x00000000FFFFFFFF);
+
+ hi = htonl (hi);
+ lo = htonl (lo);
+
+ return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
+} /* }}} uint64_t htonll */
+
+static double htond (double val) /* {{{ */
+{
+ static int config = 0;
+
+ union { uint8_t byte[8]; double floating; } in;
+ union { uint8_t byte[8]; double floating; } out;
+
+ if (config == 0)
+ {
+ double d = 8.642135e130;
+ uint8_t c[8];
+
+ memcpy (c, &d, 8);
+
+ if ((c[0] == 0x2f) && (c[1] == 0x25)
+ && (c[2] == 0xc0) && (c[3] == 0xc7)
+ && (c[4] == 0x43) && (c[5] == 0x2b)
+ && (c[6] == 0x1f) && (c[7] == 0x5b))
+ config = 1; /* need nothing */
+ else if ((c[7] == 0x2f) && (c[6] == 0x25)
+ && (c[5] == 0xc0) && (c[4] == 0xc7)
+ && (c[3] == 0x43) && (c[2] == 0x2b)
+ && (c[1] == 0x1f) && (c[0] == 0x5b))
+ config = 2; /* endian flip */
+ else if ((c[4] == 0x2f) && (c[5] == 0x25)
+ && (c[6] == 0xc0) && (c[7] == 0xc7)
+ && (c[0] == 0x43) && (c[1] == 0x2b)
+ && (c[2] == 0x1f) && (c[3] == 0x5b))
+ config = 3; /* int swap */
+ else
+ config = 4;
+ }
+
+ if (isnan (val))
+ {
+ out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
+ out.byte[4] = out.byte[5] = 0x00;
+ out.byte[6] = 0xf8;
+ out.byte[7] = 0x7f;
+ return (out.floating);
+ }
+ else if (config == 1)
+ return (val);
+ else if (config == 2)
+ {
+ in.floating = val;
+ out.byte[0] = in.byte[7];
+ out.byte[1] = in.byte[6];
+ out.byte[2] = in.byte[5];
+ out.byte[3] = in.byte[4];
+ out.byte[4] = in.byte[3];
+ out.byte[5] = in.byte[2];
+ out.byte[6] = in.byte[1];
+ out.byte[7] = in.byte[0];
+ return (out.floating);
+ }
+ else if (config == 3)
+ {
+ in.floating = val;
+ out.byte[0] = in.byte[4];
+ out.byte[1] = in.byte[5];
+ out.byte[2] = in.byte[6];
+ out.byte[3] = in.byte[7];
+ out.byte[4] = in.byte[0];
+ out.byte[5] = in.byte[1];
+ out.byte[6] = in.byte[2];
+ out.byte[7] = in.byte[3];
+ return (out.floating);
+ }
+ else
+ {
+ /* If in doubt, just copy the value back to the caller. */
+ return (val);
+ }
+} /* }}} double htond */
+
+static int nb_add_values (char **ret_buffer, /* {{{ */
+ size_t *ret_buffer_len,
+ const lcc_value_list_t *vl)
+{
+ char *packet_ptr;
+ size_t packet_len;
+
+ uint16_t pkg_type;
+ uint16_t pkg_length;
+ uint16_t pkg_num_values;
+ uint8_t pkg_values_types[vl->values_len];
+ value_t pkg_values[vl->values_len];
+
+ size_t offset;
+ size_t i;
+
+ packet_len = sizeof (pkg_type) + sizeof (pkg_length)
+ + sizeof (pkg_num_values)
+ + sizeof (pkg_values_types)
+ + sizeof (pkg_values);
+
+ if (*ret_buffer_len < packet_len)
+ return (ENOMEM);
+
+ pkg_type = htons (TYPE_VALUES);
+ pkg_length = htons ((uint16_t) packet_len);
+ pkg_num_values = htons ((uint16_t) vl->values_len);
+
+ for (i = 0; i < vl->values_len; i++)
+ {
+ pkg_values_types[i] = (uint8_t) vl->values_types[i];
+ switch (vl->values_types[i])
+ {
+ case LCC_TYPE_COUNTER:
+ pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
+ break;
+
+ case LCC_TYPE_GAUGE:
+ pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
+ break;
+
+ case LCC_TYPE_DERIVE:
+ pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
+ break;
+
+ case LCC_TYPE_ABSOLUTE:
+ pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
+ break;
+
+ default:
+ return (EINVAL);
+ } /* switch (vl->values_types[i]) */
+ } /* for (vl->values_len) */
+
+ /*
+ * Use `memcpy' to write everything to the buffer, because the pointer
+ * may be unaligned and some architectures, such as SPARC, can't handle
+ * that.
+ */
+ packet_ptr = *ret_buffer;
+ offset = 0;
+ memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+ offset += sizeof (pkg_type);
+ memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+ offset += sizeof (pkg_length);
+ memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
+ offset += sizeof (pkg_num_values);
+ memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
+ offset += sizeof (pkg_values_types);
+ memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
+ offset += sizeof (pkg_values);
+
+ assert (offset == packet_len);
+
+ *ret_buffer = packet_ptr + packet_len;
+ *ret_buffer_len -= packet_len;
+ return (0);
+} /* }}} int nb_add_values */
+
+static int nb_add_number (char **ret_buffer, /* {{{ */
+ size_t *ret_buffer_len,
+ uint16_t type, uint64_t value)
+{
+ char *packet_ptr;
+ size_t packet_len;
+
+ uint16_t pkg_type;
+ uint16_t pkg_length;
+ uint64_t pkg_value;
+
+ size_t offset;
+
+ packet_len = sizeof (pkg_type)
+ + sizeof (pkg_length)
+ + sizeof (pkg_value);
+
+ if (*ret_buffer_len < packet_len)
+ return (ENOMEM);
+
+ pkg_type = htons (type);
+ pkg_length = htons ((uint16_t) packet_len);
+ pkg_value = htonll (value);
+
+ packet_ptr = *ret_buffer;
+ offset = 0;
+ memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+ offset += sizeof (pkg_type);
+ memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+ offset += sizeof (pkg_length);
+ memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
+ offset += sizeof (pkg_value);
+
+ assert (offset == packet_len);
+
+ *ret_buffer = packet_ptr + packet_len;
+ *ret_buffer_len -= packet_len;
+ return (0);
+} /* }}} int nb_add_number */
+
+static int nb_add_string (char **ret_buffer, /* {{{ */
+ size_t *ret_buffer_len,
+ uint16_t type, const char *str, size_t str_len)
+{
+ char *packet_ptr;
+ size_t packet_len;
+
+ uint16_t pkg_type;
+ uint16_t pkg_length;
+
+ size_t offset;
+
+ packet_len = sizeof (pkg_type)
+ + sizeof (pkg_length)
+ + str_len + 1;
+ if (*ret_buffer_len < packet_len)
+ return (ENOMEM);
+
+ pkg_type = htons (type);
+ pkg_length = htons ((uint16_t) packet_len);
+
+ packet_ptr = *ret_buffer;
+ offset = 0;
+ memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
+ offset += sizeof (pkg_type);
+ memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
+ offset += sizeof (pkg_length);
+ memcpy (packet_ptr + offset, str, str_len);
+ offset += str_len;
+ memset (packet_ptr + offset, 0, 1);
+ offset += 1;
+
+ assert (offset == packet_len);
+
+ *ret_buffer = packet_ptr + packet_len;
+ *ret_buffer_len -= packet_len;
+ return (0);
+} /* }}} int nb_add_string */
+
+static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
+ const lcc_value_list_t *vl)
+{
+ char *buffer = nb->ptr;
+ size_t buffer_size = nb->free;
+
+ const lcc_identifier_t *ident_src;
+ lcc_identifier_t *ident_dst;
+
+ ident_src = &vl->identifier;
+ ident_dst = &nb->state.identifier;
+
+ if (strcmp (ident_dst->host, ident_src->host) != 0)
+ {
+ if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
+ ident_src->host, strlen (ident_src->host)) != 0)
+ return (-1);
+ SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
+ }
+
+ if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
+ {
+ if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
+ ident_src->plugin, strlen (ident_src->plugin)) != 0)
+ return (-1);
+ SSTRNCPY (ident_dst->plugin, ident_src->plugin,
+ sizeof (ident_dst->plugin));
+ }
+
+ if (strcmp (ident_dst->plugin_instance,
+ ident_src->plugin_instance) != 0)
+ {
+ if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
+ ident_src->plugin_instance,
+ strlen (ident_src->plugin_instance)) != 0)
+ return (-1);
+ SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
+ sizeof (ident_dst->plugin_instance));
+ }
+
+ if (strcmp (ident_dst->type, ident_src->type) != 0)
+ {
+ if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
+ ident_src->type, strlen (ident_src->type)) != 0)
+ return (-1);
+ SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
+ }
+
+ if (strcmp (ident_dst->type_instance,
+ ident_src->type_instance) != 0)
+ {
+ if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
+ ident_src->type_instance,
+ strlen (ident_src->type_instance)) != 0)
+ return (-1);
+ SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
+ sizeof (ident_dst->type_instance));
+ }
+
+ if (nb->state.time != vl->time)
+ {
+ if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
+ (uint64_t) vl->time))
+ return (-1);
+ nb->state.time = vl->time;
+ }
+
+ if (nb->state.interval != vl->interval)
+ {
+ if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL,
+ (uint64_t) vl->interval))
+ return (-1);
+ nb->state.interval = vl->interval;
+ }
+
+ if (nb_add_values (&buffer, &buffer_size, vl) != 0)
+ return (-1);
+
+ nb->ptr = buffer;
+ nb->free = buffer_size;
+ return (0);
+} /* }}} int nb_add_value_list */
+
+static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
+{
+ char *buffer;
+ size_t buffer_size;
+
+ gcry_md_hd_t hd;
+ gcry_error_t err;
+ unsigned char *hash;
+ const size_t hash_length = 32;
+
+ /* The type, length and username have already been filled in by
+ * "lcc_network_buffer_initialize". All we do here is calculate the hash over
+ * the username and the data and add the hash value to the buffer. */
+
+ buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
+ assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
+ buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
+
+ hd = NULL;
+ err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err != 0)
+ return (-1);
+
+ assert (nb->password != NULL);
+ err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
+ if (err != 0)
+ {
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ gcry_md_write (hd, buffer, buffer_size);
+ hash = gcry_md_read (hd, GCRY_MD_SHA256);
+ if (hash == NULL)
+ {
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
+ memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
+
+ gcry_md_close (hd);
+ return (0);
+} /* }}} int nb_add_signature */
+
+static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
+{
+ size_t package_length;
+ char *encr_ptr; /* pointer to data being encrypted */
+ size_t encr_size;
+
+ char *hash_ptr; /* pointer to data being hashed */
+ size_t hash_size;
+ char hash[20];
+
+ uint16_t pkg_length;
+ gcry_error_t err;
+
+ /* Fill in the package length */
+ package_length = nb->size - nb->free;
+ pkg_length = htons ((uint16_t) package_length);
+ memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
+
+ /* Calculate what to hash */
+ hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
+ hash_size = package_length - nb->encr_header_len;
+
+ /* Calculate what to encrypt */
+ encr_ptr = hash_ptr - sizeof (hash);
+ encr_size = hash_size + sizeof (hash);
+
+ /* Calculate the SHA-1 hash */
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
+ memcpy (encr_ptr, hash, sizeof (hash));
+
+ if (nb->encr_cypher == NULL)
+ {
+ unsigned char password_hash[32];
+
+ err = gcry_cipher_open (&nb->encr_cypher,
+ GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
+ if (err != 0)
+ return (-1);
+
+ /* Calculate our 256bit key used for AES */
+ gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
+ nb->password, strlen (nb->password));
+
+ err = gcry_cipher_setkey (nb->encr_cypher,
+ password_hash, sizeof (password_hash));
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+ }
+ else /* if (nb->encr_cypher != NULL) */
+ {
+ gcry_cipher_reset (nb->encr_cypher);
+ }
+
+ /* Set the initialization vector */
+ err = gcry_cipher_setiv (nb->encr_cypher,
+ nb->encr_iv, sizeof (nb->encr_iv));
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+
+ /* Encrypt the buffer in-place */
+ err = gcry_cipher_encrypt (nb->encr_cypher,
+ encr_ptr, encr_size,
+ /* in = */ NULL, /* in len = */ 0);
+ if (err != 0)
+ {
+ gcry_cipher_close (nb->encr_cypher);
+ nb->encr_cypher = NULL;
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int nb_add_encryption */
+
+/*
+ * Public functions
+ */
+lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
+{
+ lcc_network_buffer_t *nb;
+
+ if (size == 0)
+ size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
+
+ if (size < 128)
+ {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ nb = malloc (sizeof (*nb));
+ if (nb == NULL)
+ return (NULL);
+ memset (nb, 0, sizeof (*nb));
+
+ nb->size = size;
+ nb->buffer = malloc (nb->size);
+ if (nb->buffer == NULL)
+ {
+ free (nb);
+ return (NULL);
+ }
+ memset (nb->buffer, 0, nb->size);
+
+ nb->ptr = nb->buffer;
+ nb->free = nb->size;
+
+ nb->seclevel = NONE;
+ nb->username = NULL;
+ nb->password = NULL;
+
+ return (nb);
+} /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
+
+void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
+{
+ if (nb == NULL)
+ return;
+
+ free (nb->buffer);
+ free (nb);
+} /* }}} void lcc_network_buffer_destroy */
+
+int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
+ lcc_security_level_t level,
+ const char *username, const char *password)
+{
+ char *username_copy;
+ char *password_copy;
+
+ if (level == NONE)
+ {
+ free (nb->username);
+ free (nb->password);
+ nb->username = NULL;
+ nb->password = NULL;
+ nb->seclevel = NONE;
+ lcc_network_buffer_initialize (nb);
+ return (0);
+ }
+
+ if (!have_gcrypt ())
+ return (ENOTSUP);
+
+ username_copy = strdup (username);
+ password_copy = strdup (password);
+ if ((username_copy == NULL) || (password_copy == NULL))
+ {
+ free (username_copy);
+ free (password_copy);
+ return (ENOMEM);
+ }
+
+ free (nb->username);
+ free (nb->password);
+ nb->username = username_copy;
+ nb->password = password_copy;
+ nb->seclevel = level;
+
+ lcc_network_buffer_initialize (nb);
+ return (0);
+} /* }}} int lcc_network_buffer_set_security_level */
+
+int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
+{
+ if (nb == NULL)
+ return (EINVAL);
+
+ memset (nb->buffer, 0, nb->size);
+ memset (&nb->state, 0, sizeof (nb->state));
+ nb->ptr = nb->buffer;
+ nb->free = nb->size;
+
+ if (nb->seclevel == SIGN)
+ {
+ size_t username_len;
+ uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
+ uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
+
+ assert (nb->username != NULL);
+ username_len = strlen (nb->username);
+ pkg_length = htons (pkg_length + ((uint16_t) username_len));
+
+ /* Fill in everything but the hash value here. */
+ memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
+ memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
+ nb->ptr += PART_SIGNATURE_SHA256_SIZE;
+ nb->free -= PART_SIGNATURE_SHA256_SIZE;
+
+ memcpy (nb->ptr, nb->username, username_len);
+ nb->ptr += username_len;
+ nb->free -= username_len;
+ }
+ else if (nb->seclevel == ENCRYPT)
+ {
+ size_t username_length = strlen (nb->username);
+ uint16_t pkg_type = htons (TYPE_ENCR_AES256);
+ uint16_t pkg_length = 0; /* Filled in in finalize. */
+ uint16_t pkg_user_len = htons ((uint16_t) username_length);
+ char hash[20];
+
+ nb->encr_header_len = username_length;
+ nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
+
+ gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
+ GCRY_STRONG_RANDOM);
+
+ /* Filled in in finalize. */
+ memset (hash, 0, sizeof (hash));
+
+ ADD_STATIC (nb, pkg_type);
+ ADD_STATIC (nb, pkg_length);
+ ADD_STATIC (nb, pkg_user_len);
+ ADD_GENERIC (nb, nb->username, username_length);
+ ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
+ ADD_GENERIC (nb, hash, sizeof (hash));
+ assert ((nb->encr_header_len + nb->free) == nb->size);
+ }
+
+ return (0);
+} /* }}} int lcc_network_buffer_initialize */
+
+int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
+{
+ if (nb == NULL)
+ return (EINVAL);
+
+ if (nb->seclevel == SIGN)
+ nb_add_signature (nb);
+ else if (nb->seclevel == ENCRYPT)
+ nb_add_encryption (nb);
+
+ return (0);
+} /* }}} int lcc_network_buffer_finalize */
+
+int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
+ const lcc_value_list_t *vl)
+{
+ int status;
+
+ if ((nb == NULL) || (vl == NULL))
+ return (EINVAL);
+
+ status = nb_add_value_list (nb, vl);
+ return (status);
+} /* }}} int lcc_network_buffer_add_value */
+
+int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
+ void *buffer, size_t *buffer_size)
+{
+ size_t sz_required;
+ size_t sz_available;
+
+ if ((nb == NULL) || (buffer_size == NULL))
+ return (EINVAL);
+
+ assert (nb->size >= nb->free);
+ sz_required = nb->size - nb->free;
+ sz_available = *buffer_size;
+
+ *buffer_size = sz_required;
+ if (buffer != NULL)
+ memcpy (buffer, nb->buffer,
+ (sz_available < sz_required) ? sz_available : sz_required);
+
+ return (0);
+} /* }}} int lcc_network_buffer_get */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */