Merge branch 'ff/netlib'
authorFlorian Forster <octo@collectd.org>
Sat, 17 Nov 2012 07:25:01 +0000 (08:25 +0100)
committerFlorian Forster <octo@collectd.org>
Sat, 17 Nov 2012 07:25:01 +0000 (08:25 +0100)
Conflicts:
src/collectdctl.c
src/libcollectdclient/client.c
src/libcollectdclient/collectd/client.h

15 files changed:
configure.in
src/Makefile.am
src/collectd-nagios.c
src/collectd-tg.c [new file with mode: 0644]
src/collectdctl.c
src/libcollectdclient/Makefile.am
src/libcollectdclient/client.c
src/libcollectdclient/client.h [deleted file]
src/libcollectdclient/collectd/client.h [new file with mode: 0644]
src/libcollectdclient/collectd/lcc_features.h.in [new file with mode: 0644]
src/libcollectdclient/collectd/network.h [new file with mode: 0644]
src/libcollectdclient/collectd/network_buffer.h [new file with mode: 0644]
src/libcollectdclient/lcc_features.h.in [deleted file]
src/libcollectdclient/network.c [new file with mode: 0644]
src/libcollectdclient/network_buffer.c [new file with mode: 0644]

index 4a15976..7c4ec3a 100644 (file)
@@ -5102,7 +5102,7 @@ AC_SUBST(LCC_VERSION_PATCH)
 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/libcollectdclient/Makefile src/libcollectdclient/libcollectdclient.pc src/liboconfig/Makefile bindings/Makefile bindings/java/Makefile)
 
index 58ab17e..3532e7b 100644 (file)
@@ -18,7 +18,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
 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 \
@@ -113,6 +113,18 @@ endif
 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 = 
 
index 88a5302..e31d95c 100644 (file)
@@ -66,7 +66,7 @@
 # endif
 #endif /* NAN_ZERO_ZERO */
 
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
 
 #define RET_OKAY     0
 #define RET_WARNING  1
diff --git a/src/collectd-tg.c b/src/collectd-tg.c
new file mode 100644 (file)
index 0000000..e5b2d1f
--- /dev/null
@@ -0,0 +1,405 @@
+/**
+ * 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 : */
index 61b7fc5..0b8d0c1 100644 (file)
@@ -65,7 +65,7 @@
 # endif
 #endif /* NAN_ZERO_ZERO */
 
-#include "libcollectdclient/client.h"
+#include "libcollectdclient/collectd/client.h"
 
 #define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
 
index 6044145..baf7a21 100644 (file)
@@ -4,11 +4,18 @@ if COMPILER_IS_GCC
 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
index 2adf6d4..3987590 100644 (file)
@@ -27,7 +27,7 @@
 # define __attribute__(x) /**/
 #endif
 
-#include "lcc_features.h"
+#include "collectd/lcc_features.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -41,7 +41,7 @@
 #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. */
@@ -228,53 +228,6 @@ static void lcc_chomp (char *str) /* {{{ */
   }
 } /* }}} void lcc_chomp */
 
-static int lcc_identifier_cmp (const void *a, const void *b)
-{
-  const lcc_identifier_t *ident_a, *ident_b;
-
-  int status;
-
-  ident_a = a;
-  ident_b = b;
-
-  status = strcasecmp (ident_a->host, ident_b->host);
-  if (status != 0)
-    return (status);
-
-  status = strcmp (ident_a->plugin, ident_b->plugin);
-  if (status != 0)
-    return (status);
-
-  if ((*ident_a->plugin_instance != '\0') || (*ident_b->plugin_instance != '\0'))
-  {
-    if (*ident_a->plugin_instance == '\0')
-      return (-1);
-    else if (*ident_b->plugin_instance == '\0')
-      return (1);
-
-    status = strcmp (ident_a->plugin_instance, ident_b->plugin_instance);
-    if (status != 0)
-      return (status);
-  }
-
-  status = strcmp (ident_a->type, ident_b->type);
-  if (status != 0)
-    return (status);
-
-  if ((*ident_a->type_instance != '\0') || (*ident_b->type_instance != '\0'))
-  {
-    if (*ident_a->type_instance == '\0')
-      return (-1);
-    else if (*ident_b->type_instance == '\0')
-      return (1);
-
-    status = strcmp (ident_a->type_instance, ident_b->type_instance);
-    if (status != 0)
-      return (status);
-  }
-  return (0);
-} /* }}} int lcc_identifier_cmp */
-
 static void lcc_response_free (lcc_response_t *res) /* {{{ */
 {
   size_t i;
@@ -1105,6 +1058,35 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */
   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 */
+
 int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
     lcc_identifier_t *idents, size_t idents_num)
 {
@@ -1114,7 +1096,8 @@ int lcc_sort_identifiers (lcc_connection_t *c, /* {{{ */
     return (-1);
   }
 
-  qsort (idents, idents_num, sizeof (*idents), lcc_identifier_cmp);
+  qsort (idents, idents_num, sizeof (*idents),
+      (void *) lcc_identifier_compare);
   return (0);
 } /* }}} int lcc_sort_identifiers */
 
diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h
deleted file mode 100644 (file)
index 36b1d4d..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * 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);
-
-int lcc_sort_identifiers (lcc_connection_t *c,
-    lcc_identifier_t *idents, size_t idents_num);
-
-LCC_END_DECLS
-
-/* vim: set sw=2 sts=2 et : */
-#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/client.h b/src/libcollectdclient/collectd/client.h
new file mode 100644 (file)
index 0000000..b2354ff
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * 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);
+int lcc_sort_identifiers (lcc_connection_t *c,
+    lcc_identifier_t *idents, size_t idents_num);
+
+LCC_END_DECLS
+
+/* vim: set sw=2 sts=2 et : */
+#endif /* LIBCOLLECTD_COLLECTDCLIENT_H */
diff --git a/src/libcollectdclient/collectd/lcc_features.h.in b/src/libcollectdclient/collectd/lcc_features.h.in
new file mode 100644 (file)
index 0000000..3916a17
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * 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 : */
+
diff --git a/src/libcollectdclient/collectd/network.h b/src/libcollectdclient/collectd/network.h
new file mode 100644 (file)
index 0000000..ff29b9a
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * 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 */
diff --git a/src/libcollectdclient/collectd/network_buffer.h b/src/libcollectdclient/collectd/network_buffer.h
new file mode 100644 (file)
index 0000000..76ef1bf
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * 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 : */
diff --git a/src/libcollectdclient/lcc_features.h.in b/src/libcollectdclient/lcc_features.h.in
deleted file mode 100644 (file)
index 3916a17..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * 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 : */
-
diff --git a/src/libcollectdclient/network.c b/src/libcollectdclient/network.c
new file mode 100644 (file)
index 0000000..fe1d420
--- /dev/null
@@ -0,0 +1,394 @@
+/**
+ * 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 : */
diff --git a/src/libcollectdclient/network_buffer.c b/src/libcollectdclient/network_buffer.c
new file mode 100644 (file)
index 0000000..e7b43ef
--- /dev/null
@@ -0,0 +1,788 @@
+/**
+ * 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 : */