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 <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <arpa/inet.h> /* htons */
30 #include "collectd/network_buffer.h"
32 #define TYPE_HOST 0x0000
33 #define TYPE_TIME 0x0001
34 #define TYPE_PLUGIN 0x0002
35 #define TYPE_PLUGIN_INSTANCE 0x0003
36 #define TYPE_TYPE 0x0004
37 #define TYPE_TYPE_INSTANCE 0x0005
38 #define TYPE_VALUES 0x0006
39 #define TYPE_INTERVAL 0x0007
41 /* Types to transmit notifications */
42 #define TYPE_MESSAGE 0x0100
43 #define TYPE_SEVERITY 0x0101
45 #define TYPE_SIGN_SHA256 0x0200
46 #define TYPE_ENCR_AES256 0x0210
48 /*
49 * Data types
50 */
51 struct lcc_network_buffer_s
52 {
53 char *buffer;
54 size_t size;
56 lcc_value_list_t state;
57 char *ptr;
58 size_t free;
59 };
61 #define SSTRNCPY(dst,src,sz) do { \
62 strncpy ((dst), (src), (sz)); \
63 (dst)[(sz) - 1] = 0; \
64 } while (0)
66 /*
67 * Private functions
68 */
69 static uint64_t htonll (uint64_t val) /* {{{ */
70 {
71 static int config = 0;
73 uint32_t hi;
74 uint32_t lo;
76 if (config == 0)
77 {
78 uint16_t h = 0x1234;
79 uint16_t n = htons (h);
81 if (h == n)
82 config = 1;
83 else
84 config = 2;
85 }
87 if (config == 1)
88 return (val);
90 hi = (uint32_t) (val >> 32);
91 lo = (uint32_t) (val & 0x00000000FFFFFFFF);
93 hi = htonl (hi);
94 lo = htonl (lo);
96 return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
97 } /* }}} uint64_t htonll */
99 static double htond (double val) /* {{{ */
100 {
101 static int config = 0;
103 union { uint8_t byte[8]; double floating; } in;
104 union { uint8_t byte[8]; double floating; } out;
106 if (config == 0)
107 {
108 double d = 8.642135e130;
109 uint8_t c[8];
111 memcpy (c, &d, 8);
113 if ((c[0] == 0x2f) && (c[1] == 0x25)
114 && (c[2] == 0xc0) && (c[3] == 0xc7)
115 && (c[4] == 0x43) && (c[5] == 0x2b)
116 && (c[6] == 0x1f) && (c[7] == 0x5b))
117 config = 1; /* need nothing */
118 else if ((c[7] == 0x2f) && (c[6] == 0x25)
119 && (c[5] == 0xc0) && (c[4] == 0xc7)
120 && (c[3] == 0x43) && (c[2] == 0x2b)
121 && (c[1] == 0x1f) && (c[0] == 0x5b))
122 config = 2; /* endian flip */
123 else if ((c[4] == 0x2f) && (c[5] == 0x25)
124 && (c[6] == 0xc0) && (c[7] == 0xc7)
125 && (c[0] == 0x43) && (c[1] == 0x2b)
126 && (c[2] == 0x1f) && (c[3] == 0x5b))
127 config = 3; /* int swap */
128 else
129 config = 4;
130 }
132 if (isnan (val))
133 {
134 out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
135 out.byte[4] = out.byte[5] = 0x00;
136 out.byte[6] = 0xf8;
137 out.byte[7] = 0x7f;
138 return (out.floating);
139 }
140 else if (config == 1)
141 return (val);
142 else if (config == 2)
143 {
144 in.floating = val;
145 out.byte[0] = in.byte[7];
146 out.byte[1] = in.byte[6];
147 out.byte[2] = in.byte[5];
148 out.byte[3] = in.byte[4];
149 out.byte[4] = in.byte[3];
150 out.byte[5] = in.byte[2];
151 out.byte[6] = in.byte[1];
152 out.byte[7] = in.byte[0];
153 return (out.floating);
154 }
155 else if (config == 3)
156 {
157 in.floating = val;
158 out.byte[0] = in.byte[4];
159 out.byte[1] = in.byte[5];
160 out.byte[2] = in.byte[6];
161 out.byte[3] = in.byte[7];
162 out.byte[4] = in.byte[0];
163 out.byte[5] = in.byte[1];
164 out.byte[6] = in.byte[2];
165 out.byte[7] = in.byte[3];
166 return (out.floating);
167 }
168 else
169 {
170 /* If in doubt, just copy the value back to the caller. */
171 return (val);
172 }
173 } /* }}} double htond */
175 static int nb_add_values (char **ret_buffer, /* {{{ */
176 size_t *ret_buffer_len,
177 const lcc_value_list_t *vl)
178 {
179 char *packet_ptr;
180 size_t packet_len;
182 uint16_t pkg_type;
183 uint16_t pkg_length;
184 uint16_t pkg_num_values;
185 uint8_t pkg_values_types[vl->values_len];
186 value_t pkg_values[vl->values_len];
188 size_t offset;
189 size_t i;
191 packet_len = sizeof (pkg_type) + sizeof (pkg_length)
192 + sizeof (pkg_num_values)
193 + sizeof (pkg_values_types)
194 + sizeof (pkg_values);
196 if (*ret_buffer_len < packet_len)
197 return (ENOMEM);
199 pkg_type = htons (TYPE_VALUES);
200 pkg_length = htons ((uint16_t) packet_len);
201 pkg_num_values = htons ((uint16_t) vl->values_len);
203 for (i = 0; i < vl->values_len; i++)
204 {
205 pkg_values_types[i] = (uint8_t) vl->values_types[i];
206 switch (vl->values_types[i])
207 {
208 case LCC_TYPE_COUNTER:
209 pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
210 break;
212 case LCC_TYPE_GAUGE:
213 pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
214 break;
216 case LCC_TYPE_DERIVE:
217 pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
218 break;
220 case LCC_TYPE_ABSOLUTE:
221 pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
222 break;
224 default:
225 return (EINVAL);
226 } /* switch (vl->values_types[i]) */
227 } /* for (vl->values_len) */
229 /*
230 * Use `memcpy' to write everything to the buffer, because the pointer
231 * may be unaligned and some architectures, such as SPARC, can't handle
232 * that.
233 */
234 packet_ptr = *ret_buffer;
235 offset = 0;
236 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
237 offset += sizeof (pkg_type);
238 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
239 offset += sizeof (pkg_length);
240 memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
241 offset += sizeof (pkg_num_values);
242 memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
243 offset += sizeof (pkg_values_types);
244 memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
245 offset += sizeof (pkg_values);
247 assert (offset == packet_len);
249 *ret_buffer = packet_ptr + packet_len;
250 *ret_buffer_len -= packet_len;
251 return (0);
252 } /* }}} int nb_add_values */
254 static int nb_add_number (char **ret_buffer, /* {{{ */
255 size_t *ret_buffer_len,
256 uint16_t type, uint64_t value)
257 {
258 char *packet_ptr;
259 size_t packet_len;
261 uint16_t pkg_type;
262 uint16_t pkg_length;
263 uint64_t pkg_value;
265 size_t offset;
267 packet_len = sizeof (pkg_type)
268 + sizeof (pkg_length)
269 + sizeof (pkg_value);
271 if (*ret_buffer_len < packet_len)
272 return (ENOMEM);
274 pkg_type = htons (type);
275 pkg_length = htons ((uint16_t) packet_len);
276 pkg_value = htonll (value);
278 packet_ptr = *ret_buffer;
279 offset = 0;
280 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
281 offset += sizeof (pkg_type);
282 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
283 offset += sizeof (pkg_length);
284 memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
285 offset += sizeof (pkg_value);
287 assert (offset == packet_len);
289 *ret_buffer = packet_ptr + packet_len;
290 *ret_buffer_len -= packet_len;
291 return (0);
292 } /* }}} int nb_add_number */
294 static int nb_add_string (char **ret_buffer, /* {{{ */
295 size_t *ret_buffer_len,
296 uint16_t type, const char *str, size_t str_len)
297 {
298 char *packet_ptr;
299 size_t packet_len;
301 uint16_t pkg_type;
302 uint16_t pkg_length;
304 size_t offset;
306 packet_len = sizeof (pkg_type)
307 + sizeof (pkg_length)
308 + str_len + 1;
309 if (*ret_buffer_len < packet_len)
310 return (ENOMEM);
312 pkg_type = htons (type);
313 pkg_length = htons ((uint16_t) packet_len);
315 packet_ptr = *ret_buffer;
316 offset = 0;
317 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
318 offset += sizeof (pkg_type);
319 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
320 offset += sizeof (pkg_length);
321 memcpy (packet_ptr + offset, str, str_len);
322 offset += str_len;
323 memset (packet_ptr + offset, 0, 1);
324 offset += 1;
326 assert (offset == packet_len);
328 *ret_buffer = packet_ptr + packet_len;
329 *ret_buffer_len -= packet_len;
330 return (0);
331 } /* }}} int nb_add_string */
333 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
334 const lcc_value_list_t *vl)
335 {
336 char *buffer = nb->ptr;
337 size_t buffer_size = nb->free;
339 const lcc_identifier_t *ident_src;
340 lcc_identifier_t *ident_dst;
342 ident_src = &vl->identifier;
343 ident_dst = &nb->state.identifier;
345 if (strcmp (ident_dst->host, ident_src->host) != 0)
346 {
347 if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
348 ident_src->host, strlen (ident_src->host)) != 0)
349 return (-1);
350 SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
351 }
353 if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
354 {
355 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
356 ident_src->plugin, strlen (ident_src->plugin)) != 0)
357 return (-1);
358 SSTRNCPY (ident_dst->plugin, ident_src->plugin,
359 sizeof (ident_dst->plugin));
360 }
362 if (strcmp (ident_dst->plugin_instance,
363 ident_src->plugin_instance) != 0)
364 {
365 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
366 ident_src->plugin_instance,
367 strlen (ident_src->plugin_instance)) != 0)
368 return (-1);
369 SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
370 sizeof (ident_dst->plugin_instance));
371 }
373 if (strcmp (ident_dst->type, ident_src->type) != 0)
374 {
375 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
376 ident_src->type, strlen (ident_src->type)) != 0)
377 return (-1);
378 SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
379 }
381 if (strcmp (ident_dst->type_instance,
382 ident_src->type_instance) != 0)
383 {
384 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
385 ident_src->type_instance,
386 strlen (ident_src->type_instance)) != 0)
387 return (-1);
388 SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
389 sizeof (ident_dst->type_instance));
390 }
392 if (nb->state.time != vl->time)
393 {
394 if (nb_add_number (&buffer, &buffer_size, TYPE_TIME,
395 (uint64_t) vl->time))
396 return (-1);
397 nb->state.time = vl->time;
398 }
400 if (nb->state.interval != vl->interval)
401 {
402 if (nb_add_number (&buffer, &buffer_size, TYPE_INTERVAL,
403 (uint64_t) vl->interval))
404 return (-1);
405 nb->state.interval = vl->interval;
406 }
408 if (nb_add_values (&buffer, &buffer_size, vl) != 0)
409 return (-1);
411 nb->ptr = buffer;
412 nb->free = buffer_size;
413 return (0);
414 } /* }}} int nb_add_value_list */
416 /*
417 * Public functions
418 */
419 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
420 {
421 lcc_network_buffer_t *nb;
423 if (size == 0)
424 size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
426 if (size < 128)
427 {
428 errno = EINVAL;
429 return (NULL);
430 }
432 nb = malloc (sizeof (*nb));
433 if (nb == NULL)
434 return (NULL);
435 memset (nb, 0, sizeof (*nb));
437 nb->size = size;
438 nb->buffer = malloc (nb->size);
439 if (nb->buffer == NULL)
440 {
441 free (nb);
442 return (NULL);
443 }
444 memset (nb->buffer, 0, nb->size);
446 nb->ptr = nb->buffer;
447 nb->free = nb->size;
449 return (nb);
450 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
452 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
453 {
454 if (nb == NULL)
455 return;
457 free (nb->buffer);
458 free (nb);
459 } /* }}} void lcc_network_buffer_destroy */
461 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
462 lcc_security_level_t level,
463 const char *user, const char *password)
464 {
465 /* FIXME: Not yet implemented */
466 return (-1);
467 } /* }}} int lcc_network_buffer_set_security_level */
469 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
470 {
471 if (nb == NULL)
472 return (EINVAL);
474 memset (nb->buffer, 0, nb->size);
475 memset (&nb->state, 0, sizeof (nb->state));
476 nb->ptr = nb->buffer;
477 nb->free = nb->size;
479 /* FIXME: If security is enabled, reserve space for the signature /
480 * encryption block here. */
482 return (0);
483 } /* }}} int lcc_network_buffer_initialize */
485 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
486 {
487 if (nb == NULL)
488 return (EINVAL);
490 /* FIXME: If security is enabled, sign or encrypt the packet here. */
492 return (0);
493 } /* }}} int lcc_network_buffer_finalize */
495 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
496 const lcc_value_list_t *vl)
497 {
498 int status;
500 if ((nb == NULL) || (vl == NULL))
501 return (EINVAL);
503 status = nb_add_value_list (nb, vl);
504 return (status);
505 } /* }}} int lcc_network_buffer_add_value */
507 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
508 void *buffer, size_t *buffer_size)
509 {
510 size_t sz_required;
511 size_t sz_available;
513 if ((nb == NULL) || (buffer_size == NULL))
514 return (EINVAL);
516 assert (nb->size >= nb->free);
517 sz_required = nb->size - nb->free;
518 sz_available = *buffer_size;
520 *buffer_size = sz_required;
521 if (buffer != NULL)
522 memcpy (buffer, nb->buffer,
523 (sz_available < sz_required) ? sz_available : sz_required);
525 return (0);
526 } /* }}} int lcc_network_buffer_get */
528 /* vim: set sw=2 sts=2 et fdm=marker : */