8627ef089d0a15359f0e0058f5499e4232cb5109
[collectd.git] / src / libcollectdclient / network_buffer.c
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  **/
22
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 */
29
30 #include "collectd/network_buffer.h"
31
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
40
41 /* Types to transmit notifications */
42 #define TYPE_MESSAGE         0x0100
43 #define TYPE_SEVERITY        0x0101
44
45 #define TYPE_SIGN_SHA256     0x0200
46 #define TYPE_ENCR_AES256     0x0210
47
48 /*
49  * Data types
50  */
51 struct lcc_network_buffer_s
52 {
53   char *buffer;
54   size_t size;
55
56   lcc_value_list_t state;
57   char *ptr;
58   size_t free;
59 };
60
61 #define SSTRNCPY(dst,src,sz) do { \
62   strncpy ((dst), (src), (sz));   \
63   (dst)[(sz) - 1] = 0;            \
64 } while (0)
65
66 /*
67  * Private functions
68  */
69 static uint64_t htonll (uint64_t val) /* {{{ */
70 {
71   static int config = 0;
72
73   uint32_t hi;
74   uint32_t lo;
75
76   if (config == 0)
77   {
78     uint16_t h = 0x1234;
79     uint16_t n = htons (h);
80
81     if (h == n)
82       config = 1;
83     else
84       config = 2;
85   }
86
87   if (config == 1)
88     return (val);
89
90   hi = (uint32_t) (val >> 32);
91   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
92
93   hi = htonl (hi);
94   lo = htonl (lo);
95
96   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
97 } /* }}} uint64_t htonll */
98
99 static double htond (double val) /* {{{ */
100 {
101   static int config = 0;
102
103   union { uint8_t byte[8]; double floating; } in;
104   union { uint8_t byte[8]; double floating; } out;
105
106   if (config == 0)
107   {
108     double d = 8.642135e130;
109     uint8_t c[8];
110
111     memcpy (c, &d, 8);
112
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   }
131
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 */
174
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;
181
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];
187
188   size_t offset;
189   size_t i;
190
191   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
192     + sizeof (pkg_num_values)
193     + sizeof (pkg_values_types)
194     + sizeof (pkg_values);
195
196   if (*ret_buffer_len < packet_len)
197     return (ENOMEM);
198
199   pkg_type = htons (TYPE_VALUES);
200   pkg_length = htons ((uint16_t) packet_len);
201   pkg_num_values = htons ((uint16_t) vl->values_len);
202
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;
211
212       case LCC_TYPE_GAUGE:
213         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
214         break;
215
216       case LCC_TYPE_DERIVE:
217         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
218         break;
219
220       case LCC_TYPE_ABSOLUTE:
221         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
222         break;
223
224       default:
225         return (EINVAL);
226     } /* switch (vl->values_types[i]) */
227   } /* for (vl->values_len) */
228
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);
246
247   assert (offset == packet_len);
248
249   *ret_buffer = packet_ptr + packet_len;
250   *ret_buffer_len -= packet_len;
251   return (0);
252 } /* }}} int nb_add_values */
253
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;
260
261   uint16_t pkg_type;
262   uint16_t pkg_length;
263   uint64_t pkg_value;
264
265   size_t offset;
266
267   packet_len = sizeof (pkg_type)
268     + sizeof (pkg_length)
269     + sizeof (pkg_value);
270
271   if (*ret_buffer_len < packet_len)
272     return (ENOMEM);
273
274   pkg_type = htons (type);
275   pkg_length = htons ((uint16_t) packet_len);
276   pkg_value = htonll (value);
277
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);
286
287   assert (offset == packet_len);
288
289   *ret_buffer = packet_ptr + packet_len;
290   *ret_buffer_len -= packet_len;
291   return (0);
292 } /* }}} int nb_add_number */
293
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;
300
301   uint16_t pkg_type;
302   uint16_t pkg_length;
303
304   size_t offset;
305
306   packet_len = sizeof (pkg_type)
307     + sizeof (pkg_length)
308     + str_len + 1;
309   if (*ret_buffer_len < packet_len)
310     return (ENOMEM);
311
312   pkg_type = htons (type);
313   pkg_length = htons ((uint16_t) packet_len);
314
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;
325
326   assert (offset == packet_len);
327
328   *ret_buffer = packet_ptr + packet_len;
329   *ret_buffer_len -= packet_len;
330   return (0);
331 } /* }}} int nb_add_string */
332
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;
338
339   const lcc_identifier_t *ident_src;
340   lcc_identifier_t *ident_dst;
341
342   ident_src = &vl->identifier;
343   ident_dst = &nb->state.identifier;
344
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   }
352
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   }
361
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   }
372
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   }
380
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   }
391
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   }
399
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   }
407
408   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
409     return (-1);
410
411   nb->ptr = buffer;
412   nb->free = buffer_size;
413   return (0);
414 } /* }}} int nb_add_value_list */
415
416 /*
417  * Public functions
418  */
419 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
420 {
421   lcc_network_buffer_t *nb;
422
423   if (size == 0)
424     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
425
426   if (size < 128)
427   {
428     errno = EINVAL;
429     return (NULL);
430   }
431
432   nb = malloc (sizeof (*nb));
433   if (nb == NULL)
434     return (NULL);
435   memset (nb, 0, sizeof (*nb));
436
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);
445
446   nb->ptr = nb->buffer;
447   nb->free = nb->size;
448
449   return (nb);
450 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
451
452 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
453 {
454   if (nb == NULL)
455     return;
456
457   free (nb->buffer);
458   free (nb);
459 } /* }}} void lcc_network_buffer_destroy */
460
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 */
468
469 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
470 {
471   if (nb == NULL)
472     return (EINVAL);
473
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;
478
479   /* FIXME: If security is enabled, reserve space for the signature /
480    * encryption block here. */
481
482   return (0);
483 } /* }}} int lcc_network_buffer_initialize */
484
485 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
486 {
487   if (nb == NULL)
488     return (EINVAL);
489
490   /* FIXME: If security is enabled, sign or encrypt the packet here. */
491
492   return (0);
493 } /* }}} int lcc_network_buffer_finalize */
494
495 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
496     const lcc_value_list_t *vl)
497 {
498   int status;
499
500   if ((nb == NULL) || (vl == NULL))
501     return (EINVAL);
502
503   status = nb_add_value_list (nb, vl);
504   return (status);
505 } /* }}} int lcc_network_buffer_add_value */
506
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;
512
513   if ((nb == NULL) || (buffer_size == NULL))
514     return (EINVAL);
515
516   assert (nb->size >= nb->free);
517   sz_required = nb->size - nb->free;
518   sz_available = *buffer_size;
519
520   *buffer_size = sz_required;
521   if (buffer != NULL)
522     memcpy (buffer, nb->buffer,
523         (sz_available < sz_required) ? sz_available : sz_required);
524
525   return (0);
526 } /* }}} int lcc_network_buffer_get */
527
528 /* vim: set sw=2 sts=2 et fdm=marker : */