libcollectdclient: Add defines for Windows compatibility.
[collectd.git] / src / libcollectdclient / network_buffer.c
1 /**
2  * collectd - src/libcollectdclient/network_buffer.c
3  * Copyright (C) 2010-2014  Florian octo Forster
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
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.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #if HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #if WIN32
32
33 #include <windows.h>
34 #include <math.h>
35 #include <assert.h>
36
37 #else
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 #include <assert.h>
43 #include <errno.h>
44 #include <arpa/inet.h> /* htons */
45
46 #include <pthread.h>
47
48 #if HAVE_LIBGCRYPT
49 # include <pthread.h>
50 # if defined __APPLE__
51 /* default xcode compiler throws warnings even when deprecated functionality
52  * is not used. -Werror breaks the build because of erroneous warnings.
53  * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
54  */
55 #  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
56 # endif
57 /* FreeBSD's copy of libgcrypt extends the existing GCRYPT_NO_DEPRECATED
58  * to properly hide all deprecated functionality.
59  * http://svnweb.freebsd.org/ports/head/security/libgcrypt/files/patch-src__gcrypt.h.in
60  */
61 # define GCRYPT_NO_DEPRECATED
62 # include <gcrypt.h>
63 # if defined __APPLE__
64 /* Re enable deprecation warnings */
65 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
66 # endif
67 GCRY_THREAD_OPTION_PTHREAD_IMPL;
68 #endif
69
70 #endif /* !WIN32 */
71
72 #include "collectd/network_buffer.h"
73
74 #define TYPE_HOST            0x0000
75 #define TYPE_TIME            0x0001
76 #define TYPE_TIME_HR         0x0008
77 #define TYPE_PLUGIN          0x0002
78 #define TYPE_PLUGIN_INSTANCE 0x0003
79 #define TYPE_TYPE            0x0004
80 #define TYPE_TYPE_INSTANCE   0x0005
81 #define TYPE_VALUES          0x0006
82 #define TYPE_INTERVAL        0x0007
83 #define TYPE_INTERVAL_HR     0x0009
84
85 /* Types to transmit notifications */
86 #define TYPE_MESSAGE         0x0100
87 #define TYPE_SEVERITY        0x0101
88
89 #define TYPE_SIGN_SHA256     0x0200
90 #define TYPE_ENCR_AES256     0x0210
91
92 #define PART_SIGNATURE_SHA256_SIZE 36
93 #define PART_ENCRYPTION_AES256_SIZE 42
94
95 #ifndef ENOTSUP
96 # define ENOTSUP -1
97 #endif
98
99 #define ADD_GENERIC(nb,srcptr,size) do {         \
100   assert ((size) <= (nb)->free);                 \
101   memcpy ((nb)->ptr, (srcptr), (size));          \
102   (nb)->ptr += (size);                           \
103   (nb)->free -= (size);                          \
104 } while (0)
105
106 #define ADD_STATIC(nb,var) \
107   ADD_GENERIC(nb,&(var),sizeof(var));
108
109 /*
110  * Data types
111  */
112 struct lcc_network_buffer_s
113 {
114   char *buffer;
115   size_t size;
116
117   lcc_value_list_t state;
118   char *ptr;
119   size_t free;
120
121   lcc_security_level_t seclevel;
122   char *username;
123   char *password;
124
125 #if HAVE_LIBGCRYPT
126   gcry_cipher_hd_t encr_cypher;
127   size_t encr_header_len;
128   char encr_iv[16];
129 #endif
130 };
131
132 #define SSTRNCPY(dst,src,sz) do { \
133   strncpy ((dst), (src), (sz));   \
134   (dst)[(sz) - 1] = 0;            \
135 } while (0)
136
137 /*
138  * Private functions
139  */
140 static _Bool have_gcrypt (void) /* {{{ */
141 {
142   static _Bool result = 0;
143   static _Bool need_init = 1;
144
145   if (!need_init)
146     return (result);
147   need_init = 0;
148
149 #if HAVE_LIBGCRYPT
150   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
151
152   if (!gcry_check_version (GCRYPT_VERSION))
153     return (0);
154
155   gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
156   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
157
158   result = 1;
159   return (1);
160 #else
161   return(0);
162 #endif
163 } /* }}} _Bool have_gcrypt */
164
165 #ifndef HAVE_HTONLL
166 static uint64_t htonll (uint64_t val) /* {{{ */
167 {
168   static int config = 0;
169
170   uint32_t hi;
171   uint32_t lo;
172
173   if (config == 0)
174   {
175     uint16_t h = 0x1234;
176     uint16_t n = htons (h);
177
178     if (h == n)
179       config = 1;
180     else
181       config = 2;
182   }
183
184   if (config == 1)
185     return (val);
186
187   hi = (uint32_t) (val >> 32);
188   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
189
190   hi = htonl (hi);
191   lo = htonl (lo);
192
193   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
194 } /* }}} uint64_t htonll */
195 #endif
196
197 static double htond (double val) /* {{{ */
198 {
199   static int config = 0;
200
201   union { uint8_t byte[8]; double floating; } in;
202   union { uint8_t byte[8]; double floating; } out;
203
204   if (config == 0)
205   {
206     double d = 8.642135e130;
207     uint8_t c[8];
208
209     memcpy (c, &d, 8);
210
211     if ((c[0] == 0x2f) && (c[1] == 0x25)
212         && (c[2] == 0xc0) && (c[3] == 0xc7)
213         && (c[4] == 0x43) && (c[5] == 0x2b)
214         && (c[6] == 0x1f) && (c[7] == 0x5b))
215       config = 1; /* need nothing */
216     else if ((c[7] == 0x2f) && (c[6] == 0x25)
217         && (c[5] == 0xc0) && (c[4] == 0xc7)
218         && (c[3] == 0x43) && (c[2] == 0x2b)
219         && (c[1] == 0x1f) && (c[0] == 0x5b))
220       config = 2; /* endian flip */
221     else if ((c[4] == 0x2f) && (c[5] == 0x25)
222         && (c[6] == 0xc0) && (c[7] == 0xc7)
223         && (c[0] == 0x43) && (c[1] == 0x2b)
224         && (c[2] == 0x1f) && (c[3] == 0x5b))
225       config = 3; /* int swap */
226     else
227       config = 4;
228   }
229
230   if (isnan (val))
231   {
232     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
233     out.byte[4] = out.byte[5] = 0x00;
234     out.byte[6] = 0xf8;
235     out.byte[7] = 0x7f;
236     return (out.floating);
237   }
238   else if (config == 1)
239     return (val);
240   else if (config == 2)
241   {
242     in.floating = val;
243     out.byte[0] = in.byte[7];
244     out.byte[1] = in.byte[6];
245     out.byte[2] = in.byte[5];
246     out.byte[3] = in.byte[4];
247     out.byte[4] = in.byte[3];
248     out.byte[5] = in.byte[2];
249     out.byte[6] = in.byte[1];
250     out.byte[7] = in.byte[0];
251     return (out.floating);
252   }
253   else if (config == 3)
254   {
255     in.floating = val;
256     out.byte[0] = in.byte[4];
257     out.byte[1] = in.byte[5];
258     out.byte[2] = in.byte[6];
259     out.byte[3] = in.byte[7];
260     out.byte[4] = in.byte[0];
261     out.byte[5] = in.byte[1];
262     out.byte[6] = in.byte[2];
263     out.byte[7] = in.byte[3];
264     return (out.floating);
265   }
266   else
267   {
268     /* If in doubt, just copy the value back to the caller. */
269     return (val);
270   }
271 } /* }}} double htond */
272
273 static int nb_add_values (char **ret_buffer, /* {{{ */
274     size_t *ret_buffer_len,
275     const lcc_value_list_t *vl)
276 {
277   char *packet_ptr;
278   size_t packet_len;
279
280   uint16_t      pkg_type;
281   uint16_t      pkg_length;
282   uint16_t      pkg_num_values;
283   uint8_t       pkg_values_types[vl->values_len];
284   value_t       pkg_values[vl->values_len];
285
286   size_t offset;
287   size_t i;
288
289   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
290     + sizeof (pkg_num_values)
291     + sizeof (pkg_values_types)
292     + sizeof (pkg_values);
293
294   if (*ret_buffer_len < packet_len)
295     return (ENOMEM);
296
297   pkg_type = htons (TYPE_VALUES);
298   pkg_length = htons ((uint16_t) packet_len);
299   pkg_num_values = htons ((uint16_t) vl->values_len);
300
301   for (i = 0; i < vl->values_len; i++)
302   {
303     pkg_values_types[i] = (uint8_t) vl->values_types[i];
304     switch (vl->values_types[i])
305     {
306       case LCC_TYPE_COUNTER:
307         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
308         break;
309
310       case LCC_TYPE_GAUGE:
311         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
312         break;
313
314       case LCC_TYPE_DERIVE:
315         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
316         break;
317
318       case LCC_TYPE_ABSOLUTE:
319         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
320         break;
321
322       default:
323         return (EINVAL);
324     } /* switch (vl->values_types[i]) */
325   } /* for (vl->values_len) */
326
327   /*
328    * Use `memcpy' to write everything to the buffer, because the pointer
329    * may be unaligned and some architectures, such as SPARC, can't handle
330    * that.
331    */
332   packet_ptr = *ret_buffer;
333   offset = 0;
334   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
335   offset += sizeof (pkg_type);
336   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
337   offset += sizeof (pkg_length);
338   memcpy (packet_ptr + offset, &pkg_num_values, sizeof (pkg_num_values));
339   offset += sizeof (pkg_num_values);
340   memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
341   offset += sizeof (pkg_values_types);
342   memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
343   offset += sizeof (pkg_values);
344
345   assert (offset == packet_len);
346
347   *ret_buffer = packet_ptr + packet_len;
348   *ret_buffer_len -= packet_len;
349   return (0);
350 } /* }}} int nb_add_values */
351
352 static int nb_add_number (char **ret_buffer, /* {{{ */
353     size_t *ret_buffer_len,
354     uint16_t type, uint64_t value)
355 {
356   char *packet_ptr;
357   size_t packet_len;
358
359   uint16_t pkg_type;
360   uint16_t pkg_length;
361   uint64_t pkg_value;
362
363   size_t offset;
364
365   packet_len = sizeof (pkg_type)
366     + sizeof (pkg_length)
367     + sizeof (pkg_value);
368
369   if (*ret_buffer_len < packet_len)
370     return (ENOMEM);
371
372   pkg_type = htons (type);
373   pkg_length = htons ((uint16_t) packet_len);
374   pkg_value = htonll (value);
375
376   packet_ptr = *ret_buffer;
377   offset = 0;
378   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
379   offset += sizeof (pkg_type);
380   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
381   offset += sizeof (pkg_length);
382   memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
383   offset += sizeof (pkg_value);
384
385   assert (offset == packet_len);
386
387   *ret_buffer = packet_ptr + packet_len;
388   *ret_buffer_len -= packet_len;
389   return (0);
390 } /* }}} int nb_add_number */
391
392 static int nb_add_time (char **ret_buffer, /* {{{ */
393     size_t *ret_buffer_len,
394     uint16_t type, double value)
395 {
396   /* Convert to collectd's "cdtime" representation. */
397   uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
398   return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
399 } /* }}} int nb_add_time */
400
401 static int nb_add_string (char **ret_buffer, /* {{{ */
402     size_t *ret_buffer_len,
403     uint16_t type, const char *str, size_t str_len)
404 {
405   char *packet_ptr;
406   size_t packet_len;
407
408   uint16_t pkg_type;
409   uint16_t pkg_length;
410
411   size_t offset;
412
413   packet_len = sizeof (pkg_type)
414     + sizeof (pkg_length)
415     + str_len + 1;
416   if (*ret_buffer_len < packet_len)
417     return (ENOMEM);
418
419   pkg_type = htons (type);
420   pkg_length = htons ((uint16_t) packet_len);
421
422   packet_ptr = *ret_buffer;
423   offset = 0;
424   memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
425   offset += sizeof (pkg_type);
426   memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
427   offset += sizeof (pkg_length);
428   memcpy (packet_ptr + offset, str, str_len);
429   offset += str_len;
430   memset (packet_ptr + offset, 0, 1);
431   offset += 1;
432
433   assert (offset == packet_len);
434
435   *ret_buffer = packet_ptr + packet_len;
436   *ret_buffer_len -= packet_len;
437   return (0);
438 } /* }}} int nb_add_string */
439
440 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
441     const lcc_value_list_t *vl)
442 {
443   char *buffer = nb->ptr;
444   size_t buffer_size = nb->free;
445
446   const lcc_identifier_t *ident_src;
447   lcc_identifier_t *ident_dst;
448
449   ident_src = &vl->identifier;
450   ident_dst = &nb->state.identifier;
451
452   if (strcmp (ident_dst->host, ident_src->host) != 0)
453   {
454     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
455           ident_src->host, strlen (ident_src->host)) != 0)
456       return (-1);
457     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
458   }
459
460   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
461   {
462     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
463           ident_src->plugin, strlen (ident_src->plugin)) != 0)
464       return (-1);
465     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
466         sizeof (ident_dst->plugin));
467   }
468
469   if (strcmp (ident_dst->plugin_instance,
470         ident_src->plugin_instance) != 0)
471   {
472     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
473           ident_src->plugin_instance,
474           strlen (ident_src->plugin_instance)) != 0)
475       return (-1);
476     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
477         sizeof (ident_dst->plugin_instance));
478   }
479
480   if (strcmp (ident_dst->type, ident_src->type) != 0)
481   {
482     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
483           ident_src->type, strlen (ident_src->type)) != 0)
484       return (-1);
485     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
486   }
487
488   if (strcmp (ident_dst->type_instance,
489         ident_src->type_instance) != 0)
490   {
491     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
492           ident_src->type_instance,
493           strlen (ident_src->type_instance)) != 0)
494       return (-1);
495     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
496         sizeof (ident_dst->type_instance));
497   }
498
499   if (nb->state.time != vl->time)
500   {
501     if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
502       return (-1);
503     nb->state.time = vl->time;
504   }
505
506   if (nb->state.interval != vl->interval)
507   {
508     if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
509       return (-1);
510     nb->state.interval = vl->interval;
511   }
512
513   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
514     return (-1);
515
516   nb->ptr = buffer;
517   nb->free = buffer_size;
518   return (0);
519 } /* }}} int nb_add_value_list */
520
521 /* TODO: Add encryption for Windows */
522 #if HAVE_LIBGCRYPT
523 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
524 {
525   char *buffer;
526   size_t buffer_size;
527
528   gcry_md_hd_t hd;
529   gcry_error_t err;
530   unsigned char *hash;
531   const size_t hash_length = 32;
532
533   /* The type, length and username have already been filled in by
534    * "lcc_network_buffer_initialize". All we do here is calculate the hash over
535    * the username and the data and add the hash value to the buffer. */
536
537   buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
538   assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
539   buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
540
541   hd = NULL;
542   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
543   if (err != 0)
544     return (-1);
545
546   assert (nb->password != NULL);
547   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
548   if (err != 0)
549   {
550     gcry_md_close (hd);
551     return (-1);
552   }
553
554   gcry_md_write (hd, buffer, buffer_size);
555   hash = gcry_md_read (hd, GCRY_MD_SHA256);
556   if (hash == NULL)
557   {
558     gcry_md_close (hd);
559     return (-1);
560   }
561
562   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
563   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
564
565   gcry_md_close (hd);
566   return (0);
567 } /* }}} int nb_add_signature */
568
569 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
570 {
571   size_t package_length;
572   char *encr_ptr; /* pointer to data being encrypted */
573   size_t encr_size;
574
575   char *hash_ptr; /* pointer to data being hashed */
576   size_t hash_size;
577   char hash[20];
578
579   uint16_t pkg_length;
580   gcry_error_t err;
581
582   /* Fill in the package length */
583   package_length = nb->size - nb->free;
584   pkg_length = htons ((uint16_t) package_length);
585   memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
586
587   /* Calculate what to hash */
588   hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
589   hash_size = package_length - nb->encr_header_len;
590
591   /* Calculate what to encrypt */
592   encr_ptr = hash_ptr - sizeof (hash);
593   encr_size = hash_size + sizeof (hash);
594
595   /* Calculate the SHA-1 hash */
596   gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
597   memcpy (encr_ptr, hash, sizeof (hash));
598
599   if (nb->encr_cypher == NULL)
600   {
601     unsigned char password_hash[32];
602
603     err = gcry_cipher_open (&nb->encr_cypher,
604         GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
605     if (err != 0)
606       return (-1);
607
608     /* Calculate our 256bit key used for AES */
609     gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
610         nb->password, strlen (nb->password));
611
612     err = gcry_cipher_setkey (nb->encr_cypher,
613         password_hash, sizeof (password_hash));
614     if (err != 0)
615     {
616       gcry_cipher_close (nb->encr_cypher);
617       nb->encr_cypher = NULL;
618       return (-1);
619     }
620   }
621   else /* if (nb->encr_cypher != NULL) */
622   {
623     gcry_cipher_reset (nb->encr_cypher);
624   }
625
626   /* Set the initialization vector */
627   err = gcry_cipher_setiv (nb->encr_cypher,
628       nb->encr_iv, sizeof (nb->encr_iv));
629   if (err != 0)
630   {
631     gcry_cipher_close (nb->encr_cypher);
632     nb->encr_cypher = NULL;
633     return (-1);
634   }
635
636   /* Encrypt the buffer in-place */
637   err = gcry_cipher_encrypt (nb->encr_cypher,
638       encr_ptr, encr_size,
639       /* in = */ NULL, /* in len = */ 0);
640   if (err != 0)
641   {
642     gcry_cipher_close (nb->encr_cypher);
643     nb->encr_cypher = NULL;
644     return (-1);
645   }
646
647   return (0);
648 } /* }}} int nb_add_encryption */
649 #endif
650
651 /*
652  * Public functions
653  */
654 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
655 {
656   lcc_network_buffer_t *nb;
657
658   if (size == 0)
659     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
660
661   if (size < 128)
662   {
663     errno = EINVAL;
664     return (NULL);
665   }
666
667   nb = malloc (sizeof (*nb));
668   if (nb == NULL)
669     return (NULL);
670   memset (nb, 0, sizeof (*nb));
671
672   nb->size = size;
673   nb->buffer = malloc (nb->size);
674   if (nb->buffer == NULL)
675   {
676     free (nb);
677     return (NULL);
678   }
679   memset (nb->buffer, 0, nb->size);
680
681   nb->ptr = nb->buffer;
682   nb->free = nb->size;
683
684   nb->seclevel = NONE;
685   nb->username = NULL;
686   nb->password = NULL;
687
688   return (nb);
689 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
690
691 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
692 {
693   if (nb == NULL)
694     return;
695
696   free (nb->buffer);
697   free (nb);
698 } /* }}} void lcc_network_buffer_destroy */
699
700 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
701     lcc_security_level_t level,
702     const char *username, const char *password)
703 {
704   char *username_copy;
705   char *password_copy;
706
707   if (level == NONE)
708   {
709     free (nb->username);
710     free (nb->password);
711     nb->username = NULL;
712     nb->password = NULL;
713     nb->seclevel = NONE;
714     lcc_network_buffer_initialize (nb);
715     return (0);
716   }
717
718   if (!have_gcrypt ())
719     return (ENOTSUP);
720
721   username_copy = strdup (username);
722   password_copy = strdup (password);
723   if ((username_copy == NULL) || (password_copy == NULL))
724   {
725     free (username_copy);
726     free (password_copy);
727     return (ENOMEM);
728   }
729
730   free (nb->username);
731   free (nb->password);
732   nb->username = username_copy;
733   nb->password = password_copy;
734   nb->seclevel = level;
735
736   lcc_network_buffer_initialize (nb);
737   return (0);
738 } /* }}} int lcc_network_buffer_set_security_level */
739
740 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
741 {
742   if (nb == NULL)
743     return (EINVAL);
744
745   memset (nb->buffer, 0, nb->size);
746   memset (&nb->state, 0, sizeof (nb->state));
747   nb->ptr = nb->buffer;
748   nb->free = nb->size;
749
750 #if HAVE_LIBGCRYPT
751   if (nb->seclevel == SIGN)
752   {
753     size_t username_len;
754     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
755     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
756
757     assert (nb->username != NULL);
758     username_len = strlen (nb->username);
759     pkg_length = htons (pkg_length + ((uint16_t) username_len));
760
761     /* Fill in everything but the hash value here. */
762     memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
763     memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
764     nb->ptr += PART_SIGNATURE_SHA256_SIZE;
765     nb->free -= PART_SIGNATURE_SHA256_SIZE;
766
767     memcpy (nb->ptr, nb->username, username_len);
768     nb->ptr += username_len;
769     nb->free -= username_len;
770   }
771   else if (nb->seclevel == ENCRYPT)
772   {
773     size_t username_length = strlen (nb->username);
774     uint16_t pkg_type = htons (TYPE_ENCR_AES256);
775     uint16_t pkg_length = 0; /* Filled in in finalize. */
776     uint16_t pkg_user_len = htons ((uint16_t) username_length);
777     char hash[20];
778
779     nb->encr_header_len = username_length;
780     nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
781
782     gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
783         GCRY_STRONG_RANDOM);
784
785     /* Filled in in finalize. */
786     memset (hash, 0, sizeof (hash));
787
788     ADD_STATIC (nb, pkg_type);
789     ADD_STATIC (nb, pkg_length);
790     ADD_STATIC (nb, pkg_user_len);
791     ADD_GENERIC (nb, nb->username, username_length);
792     ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
793     ADD_GENERIC (nb, hash, sizeof (hash));
794     assert ((nb->encr_header_len + nb->free) == nb->size);
795   }
796 #endif
797
798   return (0);
799 } /* }}} int lcc_network_buffer_initialize */
800
801 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
802 {
803   if (nb == NULL)
804     return (EINVAL);
805
806 #if HAVE_LIBGCRYPT
807   if (nb->seclevel == SIGN)
808     nb_add_signature (nb);
809   else if (nb->seclevel == ENCRYPT)
810     nb_add_encryption (nb);
811 #endif
812
813   return (0);
814 } /* }}} int lcc_network_buffer_finalize */
815
816 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
817     const lcc_value_list_t *vl)
818 {
819   int status;
820
821   if ((nb == NULL) || (vl == NULL))
822     return (EINVAL);
823
824   status = nb_add_value_list (nb, vl);
825   return (status);
826 } /* }}} int lcc_network_buffer_add_value */
827
828 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
829     void *buffer, size_t *buffer_size)
830 {
831   size_t sz_required;
832   size_t sz_available;
833
834   if ((nb == NULL) || (buffer_size == NULL))
835     return (EINVAL);
836
837   assert (nb->size >= nb->free);
838   sz_required = nb->size - nb->free;
839   sz_available = *buffer_size;
840
841   *buffer_size = sz_required;
842   if (buffer != NULL)
843     memcpy (buffer, nb->buffer,
844         (sz_available < sz_required) ? sz_available : sz_required);
845
846   return (0);
847 } /* }}} int lcc_network_buffer_get */
848
849 /* vim: set sw=2 sts=2 et fdm=marker : */