libcollectdclient: Update to the high-resolution time format.
[collectd.git] / src / libcollectdclient / network_buffer.c
index 1b82911..c795a10 100644 (file)
@@ -1,23 +1,27 @@
 /**
  * collectd - src/libcollectdclient/network_buffer.c
- * Copyright (C) 2010  Florian octo Forster
+ * Copyright (C) 2010-2012  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.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * 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
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
- *   Florian octo Forster <octo at verplant.org>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "config.h"
@@ -40,12 +44,14 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
 
 #define TYPE_HOST            0x0000
 #define TYPE_TIME            0x0001
+#define TYPE_TIME_HR         0x0008
 #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
+#define TYPE_INTERVAL_HR     0x0009
 
 /* Types to transmit notifications */
 #define TYPE_MESSAGE         0x0100
@@ -55,6 +61,17 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
 #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
@@ -71,6 +88,10 @@ struct lcc_network_buffer_s
   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 { \
@@ -327,6 +348,15 @@ static int nb_add_number (char **ret_buffer, /* {{{ */
   return (0);
 } /* }}} int nb_add_number */
 
+static int nb_add_time (char **ret_buffer, /* {{{ */
+    size_t *ret_buffer_len,
+    uint16_t type, double value)
+{
+  /* Convert to collectd's "cdtime" representation. */
+  uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
+  return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
+} /* }}} int nb_add_time */
+
 static int nb_add_string (char **ret_buffer, /* {{{ */
     size_t *ret_buffer_len,
     uint16_t type, const char *str, size_t str_len)
@@ -427,16 +457,14 @@ static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
 
   if (nb->state.time != vl->time)
   {
-    if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
-          (uint64_t) vl->time))
+    if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, 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))
+    if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
       return (-1);
     nb->state.interval = vl->interval;
   }
@@ -495,6 +523,87 @@ static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
   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
  */
@@ -614,9 +723,31 @@ int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
     nb->ptr += username_len;
     nb->free -= username_len;
   }
-
-  /* FIXME: If security is enabled, reserve space for the signature /
-   * encryption block here. */
+  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 */
@@ -628,7 +759,8 @@ int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
 
   if (nb->seclevel == SIGN)
     nb_add_signature (nb);
-  /* FIXME: If security is enabled, sign or encrypt the packet here. */
+  else if (nb->seclevel == ENCRYPT)
+    nb_add_encryption (nb);
 
   return (0);
 } /* }}} int lcc_network_buffer_finalize */