1b829111e2fee028fbff3cd3ed743d40bc119a9c
1 /**
2 * collectd - src/libcollectdclient/network_buffer.c
3 * Copyright (C) 2010 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation; only version 2.1 of the License is
8 * applicable.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Florian octo Forster <octo at verplant.org>
21 **/
23 #include "config.h"
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <arpa/inet.h> /* htons */
32 #include <pthread.h>
34 #if HAVE_LIBGCRYPT
35 #include <gcrypt.h>
36 GCRY_THREAD_OPTION_PTHREAD_IMPL;
37 #endif
39 #include "collectd/network_buffer.h"
41 #define TYPE_HOST 0x0000
42 #define TYPE_TIME 0x0001
43 #define TYPE_PLUGIN 0x0002
44 #define TYPE_PLUGIN_INSTANCE 0x0003
45 #define TYPE_TYPE 0x0004
46 #define TYPE_TYPE_INSTANCE 0x0005
47 #define TYPE_VALUES 0x0006
48 #define TYPE_INTERVAL 0x0007
50 /* Types to transmit notifications */
51 #define TYPE_MESSAGE 0x0100
52 #define TYPE_SEVERITY 0x0101
54 #define TYPE_SIGN_SHA256 0x0200
55 #define TYPE_ENCR_AES256 0x0210
57 #define PART_SIGNATURE_SHA256_SIZE 36
59 /*
60 * Data types
61 */
62 struct lcc_network_buffer_s
63 {
64 char *buffer;
65 size_t size;
67 lcc_value_list_t state;
68 char *ptr;
69 size_t free;
71 lcc_security_level_t seclevel;
72 char *username;
73 char *password;
74 };
76 #define SSTRNCPY(dst,src,sz) do { \
77 strncpy ((dst), (src), (sz)); \
78 (dst)[(sz) - 1] = 0; \
79 } while (0)
81 /*
82 * Private functions
83 */
84 static _Bool have_gcrypt (void) /* {{{ */
85 {
86 static _Bool result = 0;
87 static _Bool need_init = 1;
89 if (!need_init)
90 return (result);
91 need_init = 0;
93 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
95 if (!gcry_check_version (GCRYPT_VERSION))
96 return (0);
98 gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
99 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
101 result = 1;
102 return (1);
103 } /* }}} _Bool have_gcrypt */
105 static uint64_t htonll (uint64_t val) /* {{{ */
106 {
107 static int config = 0;
109 uint32_t hi;
110 uint32_t lo;
112 if (config == 0)
113 {
114 uint16_t h = 0x1234;
115 uint16_t n = htons (h);
117 if (h == n)
118 config = 1;
119 else
120 config = 2;
121 }
123 if (config == 1)
124 return (val);
126 hi = (uint32_t) (val >> 32);
127 lo = (uint32_t) (val & 0x00000000FFFFFFFF);
129 hi = htonl (hi);
130 lo = htonl (lo);
132 return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
133 } /* }}} uint64_t htonll */
135 static double htond (double val) /* {{{ */
136 {
137 static int config = 0;
139 union { uint8_t byte[8]; double floating; } in;
140 union { uint8_t byte[8]; double floating; } out;
142 if (config == 0)
143 {
144 double d = 8.642135e130;
145 uint8_t c[8];
147 memcpy (c, &d, 8);
149 if ((c[0] == 0x2f) && (c[1] == 0x25)
150 && (c[2] == 0xc0) && (c[3] == 0xc7)
151 && (c[4] == 0x43) && (c[5] == 0x2b)
152 && (c[6] == 0x1f) && (c[7] == 0x5b))
153 config = 1; /* need nothing */
154 else if ((c[7] == 0x2f) && (c[6] == 0x25)
155 && (c[5] == 0xc0) && (c[4] == 0xc7)
156 && (c[3] == 0x43) && (c[2] == 0x2b)
157 && (c[1] == 0x1f) && (c[0] == 0x5b))
158 config = 2; /* endian flip */
159 else if ((c[4] == 0x2f) && (c[5] == 0x25)
160 && (c[6] == 0xc0) && (c[7] == 0xc7)
161 && (c[0] == 0x43) && (c[1] == 0x2b)
162 && (c[2] == 0x1f) && (c[3] == 0x5b))
163 config = 3; /* int swap */
164 else
165 config = 4;
166 }
168 if (isnan (val))
169 {
170 out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
171 out.byte[4] = out.byte[5] = 0x00;
172 out.byte[6] = 0xf8;
173 out.byte[7] = 0x7f;
174 return (out.floating);
175 }
176 else if (config == 1)
177 return (val);
178 else if (config == 2)
179 {
180 in.floating = val;
181 out.byte[0] = in.byte[7];
182 out.byte[1] = in.byte[6];
183 out.byte[2] = in.byte[5];
184 out.byte[3] = in.byte[4];
185 out.byte[4] = in.byte[3];
186 out.byte[5] = in.byte[2];
187 out.byte[6] = in.byte[1];
188 out.byte[7] = in.byte[0];
189 return (out.floating);
190 }
191 else if (config == 3)
192 {
193 in.floating = val;
194 out.byte[0] = in.byte[4];
195 out.byte[1] = in.byte[5];
196 out.byte[2] = in.byte[6];
197 out.byte[3] = in.byte[7];
198 out.byte[4] = in.byte[0];
199 out.byte[5] = in.byte[1];
200 out.byte[6] = in.byte[2];
201 out.byte[7] = in.byte[3];
202 return (out.floating);
203 }
204 else
205 {
206 /* If in doubt, just copy the value back to the caller. */
207 return (val);
208 }
209 } /* }}} double htond */
211 static int nb_add_values (char **ret_buffer, /* {{{ */
212 size_t *ret_buffer_len,
213 const lcc_value_list_t *vl)
214 {
215 char *packet_ptr;
216 size_t packet_len;
218 uint16_t pkg_type;
219 uint16_t pkg_length;
220 uint16_t pkg_num_values;
221 uint8_t pkg_values_types[vl->values_len];
222 value_t pkg_values[vl->values_len];
224 size_t offset;
225 size_t i;
227 packet_len = sizeof (pkg_type) + sizeof (pkg_length)
228 + sizeof (pkg_num_values)
229 + sizeof (pkg_values_types)
230 + sizeof (pkg_values);
232 if (*ret_buffer_len < packet_len)
233 return (ENOMEM);
235 pkg_type = htons (TYPE_VALUES);
236 pkg_length = htons ((uint16_t) packet_len);
237 pkg_num_values = htons ((uint16_t) vl->values_len);
239 for (i = 0; i < vl->values_len; i++)
240 {
241 pkg_values_types[i] = (uint8_t) vl->values_types[i];
242 switch (vl->values_types[i])
243 {
244 case LCC_TYPE_COUNTER:
245 pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
246 break;
248 case LCC_TYPE_GAUGE:
249 pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
250 break;
252 case LCC_TYPE_DERIVE:
253 pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
254 break;
256 case LCC_TYPE_ABSOLUTE:
257 pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
258 break;
260 default:
261 return (EINVAL);
262 } /* switch (vl->values_types[i]) */
263 } /* for (vl->values_len) */
265 /*
266 * Use `memcpy' to write everything to the buffer, because the pointer
267 * may be unaligned and some architectures, such as SPARC, can't handle
268 * that.
269 */
270 packet_ptr = *ret_buffer;
271 offset = 0;
272 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
273 offset += sizeof (pkg_type);
274 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
275 offset += sizeof (pkg_length);
276 memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
277 offset += sizeof (pkg_num_values);
278 memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
279 offset += sizeof (pkg_values_types);
280 memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
281 offset += sizeof (pkg_values);
283 assert (offset == packet_len);
285 *ret_buffer = packet_ptr + packet_len;
286 *ret_buffer_len -= packet_len;
287 return (0);
288 } /* }}} int nb_add_values */
290 static int nb_add_number (char **ret_buffer, /* {{{ */
291 size_t *ret_buffer_len,
292 uint16_t type, uint64_t value)
293 {
294 char *packet_ptr;
295 size_t packet_len;
297 uint16_t pkg_type;
298 uint16_t pkg_length;
299 uint64_t pkg_value;
301 size_t offset;
303 packet_len = sizeof (pkg_type)
304 + sizeof (pkg_length)
305 + sizeof (pkg_value);
307 if (*ret_buffer_len < packet_len)
308 return (ENOMEM);
310 pkg_type = htons (type);
311 pkg_length = htons ((uint16_t) packet_len);
312 pkg_value = htonll (value);
314 packet_ptr = *ret_buffer;
315 offset = 0;
316 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
317 offset += sizeof (pkg_type);
318 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
319 offset += sizeof (pkg_length);
320 memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
321 offset += sizeof (pkg_value);
323 assert (offset == packet_len);
325 *ret_buffer = packet_ptr + packet_len;
326 *ret_buffer_len -= packet_len;
327 return (0);
328 } /* }}} int nb_add_number */
330 static int nb_add_string (char **ret_buffer, /* {{{ */
331 size_t *ret_buffer_len,
332 uint16_t type, const char *str, size_t str_len)
333 {
334 char *packet_ptr;
335 size_t packet_len;
337 uint16_t pkg_type;
338 uint16_t pkg_length;
340 size_t offset;
342 packet_len = sizeof (pkg_type)
343 + sizeof (pkg_length)
344 + str_len + 1;
345 if (*ret_buffer_len < packet_len)
346 return (ENOMEM);
348 pkg_type = htons (type);
349 pkg_length = htons ((uint16_t) packet_len);
351 packet_ptr = *ret_buffer;
352 offset = 0;
353 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
354 offset += sizeof (pkg_type);
355 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
356 offset += sizeof (pkg_length);
357 memcpy (packet_ptr + offset, str, str_len);
358 offset += str_len;
359 memset (packet_ptr + offset, 0, 1);
360 offset += 1;
362 assert (offset == packet_len);
364 *ret_buffer = packet_ptr + packet_len;
365 *ret_buffer_len -= packet_len;
366 return (0);
367 } /* }}} int nb_add_string */
369 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
370 const lcc_value_list_t *vl)
371 {
372 char *buffer = nb->ptr;
373 size_t buffer_size = nb->free;
375 const lcc_identifier_t *ident_src;
376 lcc_identifier_t *ident_dst;
378 ident_src = &vl->identifier;
379 ident_dst = &nb->state.identifier;
381 if (strcmp (ident_dst->host, ident_src->host) != 0)
382 {
383 if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
384 ident_src->host, strlen (ident_src->host)) != 0)
385 return (-1);
386 SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
387 }
389 if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
390 {
391 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
392 ident_src->plugin, strlen (ident_src->plugin)) != 0)
393 return (-1);
394 SSTRNCPY (ident_dst->plugin, ident_src->plugin,
395 sizeof (ident_dst->plugin));
396 }
398 if (strcmp (ident_dst->plugin_instance,
399 ident_src->plugin_instance) != 0)
400 {
401 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
402 ident_src->plugin_instance,
403 strlen (ident_src->plugin_instance)) != 0)
404 return (-1);
405 SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
406 sizeof (ident_dst->plugin_instance));
407 }
409 if (strcmp (ident_dst->type, ident_src->type) != 0)
410 {
411 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
412 ident_src->type, strlen (ident_src->type)) != 0)
413 return (-1);
414 SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
415 }
417 if (strcmp (ident_dst->type_instance,
418 ident_src->type_instance) != 0)
419 {
420 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
421 ident_src->type_instance,
422 strlen (ident_src->type_instance)) != 0)
423 return (-1);
424 SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
425 sizeof (ident_dst->type_instance));
426 }
428 if (nb->state.time != vl->time)
429 {
430 if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
431 (uint64_t) vl->time))
432 return (-1);
433 nb->state.time = vl->time;
434 }
436 if (nb->state.interval != vl->interval)
437 {
438 if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL,
439 (uint64_t) vl->interval))
440 return (-1);
441 nb->state.interval = vl->interval;
442 }
444 if (nb_add_values (&buffer, &buffer_size, vl) != 0)
445 return (-1);
447 nb->ptr = buffer;
448 nb->free = buffer_size;
449 return (0);
450 } /* }}} int nb_add_value_list */
452 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
453 {
454 char *buffer;
455 size_t buffer_size;
457 gcry_md_hd_t hd;
458 gcry_error_t err;
459 unsigned char *hash;
460 const size_t hash_length = 32;
462 /* The type, length and username have already been filled in by
463 * "lcc_network_buffer_initialize". All we do here is calculate the hash over
464 * the username and the data and add the hash value to the buffer. */
466 buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
467 assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
468 buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
470 hd = NULL;
471 err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
472 if (err != 0)
473 return (-1);
475 assert (nb->password != NULL);
476 err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
477 if (err != 0)
478 {
479 gcry_md_close (hd);
480 return (-1);
481 }
483 gcry_md_write (hd, buffer, buffer_size);
484 hash = gcry_md_read (hd, GCRY_MD_SHA256);
485 if (hash == NULL)
486 {
487 gcry_md_close (hd);
488 return (-1);
489 }
491 assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
492 memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
494 gcry_md_close (hd);
495 return (0);
496 } /* }}} int nb_add_signature */
498 /*
499 * Public functions
500 */
501 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
502 {
503 lcc_network_buffer_t *nb;
505 if (size == 0)
506 size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
508 if (size < 128)
509 {
510 errno = EINVAL;
511 return (NULL);
512 }
514 nb = malloc (sizeof (*nb));
515 if (nb == NULL)
516 return (NULL);
517 memset (nb, 0, sizeof (*nb));
519 nb->size = size;
520 nb->buffer = malloc (nb->size);
521 if (nb->buffer == NULL)
522 {
523 free (nb);
524 return (NULL);
525 }
526 memset (nb->buffer, 0, nb->size);
528 nb->ptr = nb->buffer;
529 nb->free = nb->size;
531 nb->seclevel = NONE;
532 nb->username = NULL;
533 nb->password = NULL;
535 return (nb);
536 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
538 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
539 {
540 if (nb == NULL)
541 return;
543 free (nb->buffer);
544 free (nb);
545 } /* }}} void lcc_network_buffer_destroy */
547 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
548 lcc_security_level_t level,
549 const char *username, const char *password)
550 {
551 char *username_copy;
552 char *password_copy;
554 if (level == NONE)
555 {
556 free (nb->username);
557 free (nb->password);
558 nb->username = NULL;
559 nb->password = NULL;
560 nb->seclevel = NONE;
561 lcc_network_buffer_initialize (nb);
562 return (0);
563 }
565 if (!have_gcrypt ())
566 return (ENOTSUP);
568 username_copy = strdup (username);
569 password_copy = strdup (password);
570 if ((username_copy == NULL) || (password_copy == NULL))
571 {
572 free (username_copy);
573 free (password_copy);
574 return (ENOMEM);
575 }
577 free (nb->username);
578 free (nb->password);
579 nb->username = username_copy;
580 nb->password = password_copy;
581 nb->seclevel = level;
583 lcc_network_buffer_initialize (nb);
584 return (0);
585 } /* }}} int lcc_network_buffer_set_security_level */
587 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
588 {
589 if (nb == NULL)
590 return (EINVAL);
592 memset (nb->buffer, 0, nb->size);
593 memset (&nb->state, 0, sizeof (nb->state));
594 nb->ptr = nb->buffer;
595 nb->free = nb->size;
597 if (nb->seclevel == SIGN)
598 {
599 size_t username_len;
600 uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
601 uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
603 assert (nb->username != NULL);
604 username_len = strlen (nb->username);
605 pkg_length = htons (pkg_length + ((uint16_t) username_len));
607 /* Fill in everything but the hash value here. */
608 memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
609 memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
610 nb->ptr += PART_SIGNATURE_SHA256_SIZE;
611 nb->free -= PART_SIGNATURE_SHA256_SIZE;
613 memcpy (nb->ptr, nb->username, username_len);
614 nb->ptr += username_len;
615 nb->free -= username_len;
616 }
618 /* FIXME: If security is enabled, reserve space for the signature /
619 * encryption block here. */
621 return (0);
622 } /* }}} int lcc_network_buffer_initialize */
624 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
625 {
626 if (nb == NULL)
627 return (EINVAL);
629 if (nb->seclevel == SIGN)
630 nb_add_signature (nb);
631 /* FIXME: If security is enabled, sign or encrypt the packet here. */
633 return (0);
634 } /* }}} int lcc_network_buffer_finalize */
636 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
637 const lcc_value_list_t *vl)
638 {
639 int status;
641 if ((nb == NULL) || (vl == NULL))
642 return (EINVAL);
644 status = nb_add_value_list (nb, vl);
645 return (status);
646 } /* }}} int lcc_network_buffer_add_value */
648 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
649 void *buffer, size_t *buffer_size)
650 {
651 size_t sz_required;
652 size_t sz_available;
654 if ((nb == NULL) || (buffer_size == NULL))
655 return (EINVAL);
657 assert (nb->size >= nb->free);
658 sz_required = nb->size - nb->free;
659 sz_available = *buffer_size;
661 *buffer_size = sz_required;
662 if (buffer != NULL)
663 memcpy (buffer, nb->buffer,
664 (sz_available < sz_required) ? sz_available : sz_required);
666 return (0);
667 } /* }}} int lcc_network_buffer_get */
669 /* vim: set sw=2 sts=2 et fdm=marker : */