2 * collectd - src/libcollectdclient/network_buffer.c
3 * Copyright (C) 2010-2012 Florian octo Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian octo Forster <octo at collectd.org>
34 #include <arpa/inet.h> /* htons */
40 GCRY_THREAD_OPTION_PTHREAD_IMPL;
43 #include "collectd/network_buffer.h"
45 #define TYPE_HOST 0x0000
46 #define TYPE_TIME 0x0001
47 #define TYPE_TIME_HR 0x0008
48 #define TYPE_PLUGIN 0x0002
49 #define TYPE_PLUGIN_INSTANCE 0x0003
50 #define TYPE_TYPE 0x0004
51 #define TYPE_TYPE_INSTANCE 0x0005
52 #define TYPE_VALUES 0x0006
53 #define TYPE_INTERVAL 0x0007
54 #define TYPE_INTERVAL_HR 0x0009
56 /* Types to transmit notifications */
57 #define TYPE_MESSAGE 0x0100
58 #define TYPE_SEVERITY 0x0101
60 #define TYPE_SIGN_SHA256 0x0200
61 #define TYPE_ENCR_AES256 0x0210
63 #define PART_SIGNATURE_SHA256_SIZE 36
64 #define PART_ENCRYPTION_AES256_SIZE 42
66 #define ADD_GENERIC(nb,srcptr,size) do { \
67 assert ((size) <= (nb)->free); \
68 memcpy ((nb)->ptr, (srcptr), (size)); \
69 (nb)->ptr += (size); \
70 (nb)->free -= (size); \
73 #define ADD_STATIC(nb,var) \
74 ADD_GENERIC(nb,&(var),sizeof(var));
79 struct lcc_network_buffer_s
84 lcc_value_list_t state;
88 lcc_security_level_t seclevel;
92 gcry_cipher_hd_t encr_cypher;
93 size_t encr_header_len;
97 #define SSTRNCPY(dst,src,sz) do { \
98 strncpy ((dst), (src), (sz)); \
99 (dst)[(sz) - 1] = 0; \
105 static _Bool have_gcrypt (void) /* {{{ */
107 static _Bool result = 0;
108 static _Bool need_init = 1;
114 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
116 if (!gcry_check_version (GCRYPT_VERSION))
119 gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
120 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
124 } /* }}} _Bool have_gcrypt */
126 static uint64_t htonll (uint64_t val) /* {{{ */
128 static int config = 0;
136 uint16_t n = htons (h);
147 hi = (uint32_t) (val >> 32);
148 lo = (uint32_t) (val & 0x00000000FFFFFFFF);
153 return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
154 } /* }}} uint64_t htonll */
156 static double htond (double val) /* {{{ */
158 static int config = 0;
160 union { uint8_t byte[8]; double floating; } in;
161 union { uint8_t byte[8]; double floating; } out;
165 double d = 8.642135e130;
170 if ((c[0] == 0x2f) && (c[1] == 0x25)
171 && (c[2] == 0xc0) && (c[3] == 0xc7)
172 && (c[4] == 0x43) && (c[5] == 0x2b)
173 && (c[6] == 0x1f) && (c[7] == 0x5b))
174 config = 1; /* need nothing */
175 else if ((c[7] == 0x2f) && (c[6] == 0x25)
176 && (c[5] == 0xc0) && (c[4] == 0xc7)
177 && (c[3] == 0x43) && (c[2] == 0x2b)
178 && (c[1] == 0x1f) && (c[0] == 0x5b))
179 config = 2; /* endian flip */
180 else if ((c[4] == 0x2f) && (c[5] == 0x25)
181 && (c[6] == 0xc0) && (c[7] == 0xc7)
182 && (c[0] == 0x43) && (c[1] == 0x2b)
183 && (c[2] == 0x1f) && (c[3] == 0x5b))
184 config = 3; /* int swap */
191 out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
192 out.byte[4] = out.byte[5] = 0x00;
195 return (out.floating);
197 else if (config == 1)
199 else if (config == 2)
202 out.byte[0] = in.byte[7];
203 out.byte[1] = in.byte[6];
204 out.byte[2] = in.byte[5];
205 out.byte[3] = in.byte[4];
206 out.byte[4] = in.byte[3];
207 out.byte[5] = in.byte[2];
208 out.byte[6] = in.byte[1];
209 out.byte[7] = in.byte[0];
210 return (out.floating);
212 else if (config == 3)
215 out.byte[0] = in.byte[4];
216 out.byte[1] = in.byte[5];
217 out.byte[2] = in.byte[6];
218 out.byte[3] = in.byte[7];
219 out.byte[4] = in.byte[0];
220 out.byte[5] = in.byte[1];
221 out.byte[6] = in.byte[2];
222 out.byte[7] = in.byte[3];
223 return (out.floating);
227 /* If in doubt, just copy the value back to the caller. */
230 } /* }}} double htond */
232 static int nb_add_values (char **ret_buffer, /* {{{ */
233 size_t *ret_buffer_len,
234 const lcc_value_list_t *vl)
241 uint16_t pkg_num_values;
242 uint8_t pkg_values_types[vl->values_len];
243 value_t pkg_values[vl->values_len];
248 packet_len = sizeof (pkg_type) + sizeof (pkg_length)
249 + sizeof (pkg_num_values)
250 + sizeof (pkg_values_types)
251 + sizeof (pkg_values);
253 if (*ret_buffer_len < packet_len)
256 pkg_type = htons (TYPE_VALUES);
257 pkg_length = htons ((uint16_t) packet_len);
258 pkg_num_values = htons ((uint16_t) vl->values_len);
260 for (i = 0; i < vl->values_len; i++)
262 pkg_values_types[i] = (uint8_t) vl->values_types[i];
263 switch (vl->values_types[i])
265 case LCC_TYPE_COUNTER:
266 pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
270 pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
273 case LCC_TYPE_DERIVE:
274 pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
277 case LCC_TYPE_ABSOLUTE:
278 pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
283 } /* switch (vl->values_types[i]) */
284 } /* for (vl->values_len) */
287 * Use `memcpy' to write everything to the buffer, because the pointer
288 * may be unaligned and some architectures, such as SPARC, can't handle
291 packet_ptr = *ret_buffer;
293 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
294 offset += sizeof (pkg_type);
295 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
296 offset += sizeof (pkg_length);
297 memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
298 offset += sizeof (pkg_num_values);
299 memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
300 offset += sizeof (pkg_values_types);
301 memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
302 offset += sizeof (pkg_values);
304 assert (offset == packet_len);
306 *ret_buffer = packet_ptr + packet_len;
307 *ret_buffer_len -= packet_len;
309 } /* }}} int nb_add_values */
311 static int nb_add_number (char **ret_buffer, /* {{{ */
312 size_t *ret_buffer_len,
313 uint16_t type, uint64_t value)
324 packet_len = sizeof (pkg_type)
325 + sizeof (pkg_length)
326 + sizeof (pkg_value);
328 if (*ret_buffer_len < packet_len)
331 pkg_type = htons (type);
332 pkg_length = htons ((uint16_t) packet_len);
333 pkg_value = htonll (value);
335 packet_ptr = *ret_buffer;
337 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
338 offset += sizeof (pkg_type);
339 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
340 offset += sizeof (pkg_length);
341 memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
342 offset += sizeof (pkg_value);
344 assert (offset == packet_len);
346 *ret_buffer = packet_ptr + packet_len;
347 *ret_buffer_len -= packet_len;
349 } /* }}} int nb_add_number */
351 static int nb_add_time (char **ret_buffer, /* {{{ */
352 size_t *ret_buffer_len,
353 uint16_t type, double value)
355 /* Convert to collectd's "cdtime" representation. */
356 uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
357 return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
358 } /* }}} int nb_add_time */
360 static int nb_add_string (char **ret_buffer, /* {{{ */
361 size_t *ret_buffer_len,
362 uint16_t type, const char *str, size_t str_len)
372 packet_len = sizeof (pkg_type)
373 + sizeof (pkg_length)
375 if (*ret_buffer_len < packet_len)
378 pkg_type = htons (type);
379 pkg_length = htons ((uint16_t) packet_len);
381 packet_ptr = *ret_buffer;
383 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
384 offset += sizeof (pkg_type);
385 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
386 offset += sizeof (pkg_length);
387 memcpy (packet_ptr + offset, str, str_len);
389 memset (packet_ptr + offset, 0, 1);
392 assert (offset == packet_len);
394 *ret_buffer = packet_ptr + packet_len;
395 *ret_buffer_len -= packet_len;
397 } /* }}} int nb_add_string */
399 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
400 const lcc_value_list_t *vl)
402 char *buffer = nb->ptr;
403 size_t buffer_size = nb->free;
405 const lcc_identifier_t *ident_src;
406 lcc_identifier_t *ident_dst;
408 ident_src = &vl->identifier;
409 ident_dst = &nb->state.identifier;
411 if (strcmp (ident_dst->host, ident_src->host) != 0)
413 if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
414 ident_src->host, strlen (ident_src->host)) != 0)
416 SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
419 if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
421 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
422 ident_src->plugin, strlen (ident_src->plugin)) != 0)
424 SSTRNCPY (ident_dst->plugin, ident_src->plugin,
425 sizeof (ident_dst->plugin));
428 if (strcmp (ident_dst->plugin_instance,
429 ident_src->plugin_instance) != 0)
431 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
432 ident_src->plugin_instance,
433 strlen (ident_src->plugin_instance)) != 0)
435 SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
436 sizeof (ident_dst->plugin_instance));
439 if (strcmp (ident_dst->type, ident_src->type) != 0)
441 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
442 ident_src->type, strlen (ident_src->type)) != 0)
444 SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
447 if (strcmp (ident_dst->type_instance,
448 ident_src->type_instance) != 0)
450 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
451 ident_src->type_instance,
452 strlen (ident_src->type_instance)) != 0)
454 SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
455 sizeof (ident_dst->type_instance));
458 if (nb->state.time != vl->time)
460 if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
462 nb->state.time = vl->time;
465 if (nb->state.interval != vl->interval)
467 if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
469 nb->state.interval = vl->interval;
472 if (nb_add_values (&buffer, &buffer_size, vl) != 0)
476 nb->free = buffer_size;
478 } /* }}} int nb_add_value_list */
480 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
488 const size_t hash_length = 32;
490 /* The type, length and username have already been filled in by
491 * "lcc_network_buffer_initialize". All we do here is calculate the hash over
492 * the username and the data and add the hash value to the buffer. */
494 buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
495 assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
496 buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
499 err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
503 assert (nb->password != NULL);
504 err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
511 gcry_md_write (hd, buffer, buffer_size);
512 hash = gcry_md_read (hd, GCRY_MD_SHA256);
519 assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
520 memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
524 } /* }}} int nb_add_signature */
526 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
528 size_t package_length;
529 char *encr_ptr; /* pointer to data being encrypted */
532 char *hash_ptr; /* pointer to data being hashed */
539 /* Fill in the package length */
540 package_length = nb->size - nb->free;
541 pkg_length = htons ((uint16_t) package_length);
542 memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
544 /* Calculate what to hash */
545 hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
546 hash_size = package_length - nb->encr_header_len;
548 /* Calculate what to encrypt */
549 encr_ptr = hash_ptr - sizeof (hash);
550 encr_size = hash_size + sizeof (hash);
552 /* Calculate the SHA-1 hash */
553 gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
554 memcpy (encr_ptr, hash, sizeof (hash));
556 if (nb->encr_cypher == NULL)
558 unsigned char password_hash[32];
560 err = gcry_cipher_open (&nb->encr_cypher,
561 GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
565 /* Calculate our 256bit key used for AES */
566 gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
567 nb->password, strlen (nb->password));
569 err = gcry_cipher_setkey (nb->encr_cypher,
570 password_hash, sizeof (password_hash));
573 gcry_cipher_close (nb->encr_cypher);
574 nb->encr_cypher = NULL;
578 else /* if (nb->encr_cypher != NULL) */
580 gcry_cipher_reset (nb->encr_cypher);
583 /* Set the initialization vector */
584 err = gcry_cipher_setiv (nb->encr_cypher,
585 nb->encr_iv, sizeof (nb->encr_iv));
588 gcry_cipher_close (nb->encr_cypher);
589 nb->encr_cypher = NULL;
593 /* Encrypt the buffer in-place */
594 err = gcry_cipher_encrypt (nb->encr_cypher,
596 /* in = */ NULL, /* in len = */ 0);
599 gcry_cipher_close (nb->encr_cypher);
600 nb->encr_cypher = NULL;
605 } /* }}} int nb_add_encryption */
610 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
612 lcc_network_buffer_t *nb;
615 size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
623 nb = malloc (sizeof (*nb));
626 memset (nb, 0, sizeof (*nb));
629 nb->buffer = malloc (nb->size);
630 if (nb->buffer == NULL)
635 memset (nb->buffer, 0, nb->size);
637 nb->ptr = nb->buffer;
645 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
647 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
654 } /* }}} void lcc_network_buffer_destroy */
656 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
657 lcc_security_level_t level,
658 const char *username, const char *password)
670 lcc_network_buffer_initialize (nb);
677 username_copy = strdup (username);
678 password_copy = strdup (password);
679 if ((username_copy == NULL) || (password_copy == NULL))
681 free (username_copy);
682 free (password_copy);
688 nb->username = username_copy;
689 nb->password = password_copy;
690 nb->seclevel = level;
692 lcc_network_buffer_initialize (nb);
694 } /* }}} int lcc_network_buffer_set_security_level */
696 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
701 memset (nb->buffer, 0, nb->size);
702 memset (&nb->state, 0, sizeof (nb->state));
703 nb->ptr = nb->buffer;
706 if (nb->seclevel == SIGN)
709 uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
710 uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
712 assert (nb->username != NULL);
713 username_len = strlen (nb->username);
714 pkg_length = htons (pkg_length + ((uint16_t) username_len));
716 /* Fill in everything but the hash value here. */
717 memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
718 memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
719 nb->ptr += PART_SIGNATURE_SHA256_SIZE;
720 nb->free -= PART_SIGNATURE_SHA256_SIZE;
722 memcpy (nb->ptr, nb->username, username_len);
723 nb->ptr += username_len;
724 nb->free -= username_len;
726 else if (nb->seclevel == ENCRYPT)
728 size_t username_length = strlen (nb->username);
729 uint16_t pkg_type = htons (TYPE_ENCR_AES256);
730 uint16_t pkg_length = 0; /* Filled in in finalize. */
731 uint16_t pkg_user_len = htons ((uint16_t) username_length);
734 nb->encr_header_len = username_length;
735 nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
737 gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
740 /* Filled in in finalize. */
741 memset (hash, 0, sizeof (hash));
743 ADD_STATIC (nb, pkg_type);
744 ADD_STATIC (nb, pkg_length);
745 ADD_STATIC (nb, pkg_user_len);
746 ADD_GENERIC (nb, nb->username, username_length);
747 ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
748 ADD_GENERIC (nb, hash, sizeof (hash));
749 assert ((nb->encr_header_len + nb->free) == nb->size);
753 } /* }}} int lcc_network_buffer_initialize */
755 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
760 if (nb->seclevel == SIGN)
761 nb_add_signature (nb);
762 else if (nb->seclevel == ENCRYPT)
763 nb_add_encryption (nb);
766 } /* }}} int lcc_network_buffer_finalize */
768 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
769 const lcc_value_list_t *vl)
773 if ((nb == NULL) || (vl == NULL))
776 status = nb_add_value_list (nb, vl);
778 } /* }}} int lcc_network_buffer_add_value */
780 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
781 void *buffer, size_t *buffer_size)
786 if ((nb == NULL) || (buffer_size == NULL))
789 assert (nb->size >= nb->free);
790 sz_required = nb->size - nb->free;
791 sz_available = *buffer_size;
793 *buffer_size = sz_required;
795 memcpy (buffer, nb->buffer,
796 (sz_available < sz_required) ? sz_available : sz_required);
799 } /* }}} int lcc_network_buffer_get */
801 /* vim: set sw=2 sts=2 et fdm=marker : */