libcollectdclient: Update to the high-resolution time format.
[collectd.git] / src / libcollectdclient / network_buffer.c
1 /**
2  * collectd - src/libcollectdclient/network_buffer.c
3  * Copyright (C) 2010-2012  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 #include "config.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <arpa/inet.h> /* htons */
35
36 #include <pthread.h>
37
38 #if HAVE_LIBGCRYPT
39 #include <gcrypt.h>
40 GCRY_THREAD_OPTION_PTHREAD_IMPL;
41 #endif
42
43 #include "collectd/network_buffer.h"
44
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
55
56 /* Types to transmit notifications */
57 #define TYPE_MESSAGE         0x0100
58 #define TYPE_SEVERITY        0x0101
59
60 #define TYPE_SIGN_SHA256     0x0200
61 #define TYPE_ENCR_AES256     0x0210
62
63 #define PART_SIGNATURE_SHA256_SIZE 36
64 #define PART_ENCRYPTION_AES256_SIZE 42
65
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);                          \
71 } while (0)
72
73 #define ADD_STATIC(nb,var) \
74   ADD_GENERIC(nb,&(var),sizeof(var));
75
76 /*
77  * Data types
78  */
79 struct lcc_network_buffer_s
80 {
81   char *buffer;
82   size_t size;
83
84   lcc_value_list_t state;
85   char *ptr;
86   size_t free;
87
88   lcc_security_level_t seclevel;
89   char *username;
90   char *password;
91
92   gcry_cipher_hd_t encr_cypher;
93   size_t encr_header_len;
94   char encr_iv[16];
95 };
96
97 #define SSTRNCPY(dst,src,sz) do { \
98   strncpy ((dst), (src), (sz));   \
99   (dst)[(sz) - 1] = 0;            \
100 } while (0)
101
102 /*
103  * Private functions
104  */
105 static _Bool have_gcrypt (void) /* {{{ */
106 {
107   static _Bool result = 0;
108   static _Bool need_init = 1;
109
110   if (!need_init)
111     return (result);
112   need_init = 0;
113
114   gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
115
116   if (!gcry_check_version (GCRYPT_VERSION))
117     return (0);
118
119   gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
120   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
121
122   result = 1;
123   return (1);
124 } /* }}} _Bool have_gcrypt */
125
126 static uint64_t htonll (uint64_t val) /* {{{ */
127 {
128   static int config = 0;
129
130   uint32_t hi;
131   uint32_t lo;
132
133   if (config == 0)
134   {
135     uint16_t h = 0x1234;
136     uint16_t n = htons (h);
137
138     if (h == n)
139       config = 1;
140     else
141       config = 2;
142   }
143
144   if (config == 1)
145     return (val);
146
147   hi = (uint32_t) (val >> 32);
148   lo = (uint32_t) (val & 0x00000000FFFFFFFF);
149
150   hi = htonl (hi);
151   lo = htonl (lo);
152
153   return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
154 } /* }}} uint64_t htonll */
155
156 static double htond (double val) /* {{{ */
157 {
158   static int config = 0;
159
160   union { uint8_t byte[8]; double floating; } in;
161   union { uint8_t byte[8]; double floating; } out;
162
163   if (config == 0)
164   {
165     double d = 8.642135e130;
166     uint8_t c[8];
167
168     memcpy (c, &d, 8);
169
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 */
185     else
186       config = 4;
187   }
188
189   if (isnan (val))
190   {
191     out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
192     out.byte[4] = out.byte[5] = 0x00;
193     out.byte[6] = 0xf8;
194     out.byte[7] = 0x7f;
195     return (out.floating);
196   }
197   else if (config == 1)
198     return (val);
199   else if (config == 2)
200   {
201     in.floating = val;
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);
211   }
212   else if (config == 3)
213   {
214     in.floating = val;
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);
224   }
225   else
226   {
227     /* If in doubt, just copy the value back to the caller. */
228     return (val);
229   }
230 } /* }}} double htond */
231
232 static int nb_add_values (char **ret_buffer, /* {{{ */
233     size_t *ret_buffer_len,
234     const lcc_value_list_t *vl)
235 {
236   char *packet_ptr;
237   size_t packet_len;
238
239   uint16_t      pkg_type;
240   uint16_t      pkg_length;
241   uint16_t      pkg_num_values;
242   uint8_t       pkg_values_types[vl->values_len];
243   value_t       pkg_values[vl->values_len];
244
245   size_t offset;
246   size_t i;
247
248   packet_len = sizeof (pkg_type) + sizeof (pkg_length)
249     + sizeof (pkg_num_values)
250     + sizeof (pkg_values_types)
251     + sizeof (pkg_values);
252
253   if (*ret_buffer_len < packet_len)
254     return (ENOMEM);
255
256   pkg_type = htons (TYPE_VALUES);
257   pkg_length = htons ((uint16_t) packet_len);
258   pkg_num_values = htons ((uint16_t) vl->values_len);
259
260   for (i = 0; i < vl->values_len; i++)
261   {
262     pkg_values_types[i] = (uint8_t) vl->values_types[i];
263     switch (vl->values_types[i])
264     {
265       case LCC_TYPE_COUNTER:
266         pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
267         break;
268
269       case LCC_TYPE_GAUGE:
270         pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
271         break;
272
273       case LCC_TYPE_DERIVE:
274         pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
275         break;
276
277       case LCC_TYPE_ABSOLUTE:
278         pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
279         break;
280
281       default:
282         return (EINVAL);
283     } /* switch (vl->values_types[i]) */
284   } /* for (vl->values_len) */
285
286   /*
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
289    * that.
290    */
291   packet_ptr = *ret_buffer;
292   offset = 0;
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);
303
304   assert (offset == packet_len);
305
306   *ret_buffer = packet_ptr + packet_len;
307   *ret_buffer_len -= packet_len;
308   return (0);
309 } /* }}} int nb_add_values */
310
311 static int nb_add_number (char **ret_buffer, /* {{{ */
312     size_t *ret_buffer_len,
313     uint16_t type, uint64_t value)
314 {
315   char *packet_ptr;
316   size_t packet_len;
317
318   uint16_t pkg_type;
319   uint16_t pkg_length;
320   uint64_t pkg_value;
321
322   size_t offset;
323
324   packet_len = sizeof (pkg_type)
325     + sizeof (pkg_length)
326     + sizeof (pkg_value);
327
328   if (*ret_buffer_len < packet_len)
329     return (ENOMEM);
330
331   pkg_type = htons (type);
332   pkg_length = htons ((uint16_t) packet_len);
333   pkg_value = htonll (value);
334
335   packet_ptr = *ret_buffer;
336   offset = 0;
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);
343
344   assert (offset == packet_len);
345
346   *ret_buffer = packet_ptr + packet_len;
347   *ret_buffer_len -= packet_len;
348   return (0);
349 } /* }}} int nb_add_number */
350
351 static int nb_add_time (char **ret_buffer, /* {{{ */
352     size_t *ret_buffer_len,
353     uint16_t type, double value)
354 {
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 */
359
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)
363 {
364   char *packet_ptr;
365   size_t packet_len;
366
367   uint16_t pkg_type;
368   uint16_t pkg_length;
369
370   size_t offset;
371
372   packet_len = sizeof (pkg_type)
373     + sizeof (pkg_length)
374     + str_len + 1;
375   if (*ret_buffer_len < packet_len)
376     return (ENOMEM);
377
378   pkg_type = htons (type);
379   pkg_length = htons ((uint16_t) packet_len);
380
381   packet_ptr = *ret_buffer;
382   offset = 0;
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);
388   offset += str_len;
389   memset (packet_ptr + offset, 0, 1);
390   offset += 1;
391
392   assert (offset == packet_len);
393
394   *ret_buffer = packet_ptr + packet_len;
395   *ret_buffer_len -= packet_len;
396   return (0);
397 } /* }}} int nb_add_string */
398
399 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
400     const lcc_value_list_t *vl)
401 {
402   char *buffer = nb->ptr;
403   size_t buffer_size = nb->free;
404
405   const lcc_identifier_t *ident_src;
406   lcc_identifier_t *ident_dst;
407
408   ident_src = &vl->identifier;
409   ident_dst = &nb->state.identifier;
410
411   if (strcmp (ident_dst->host, ident_src->host) != 0)
412   {
413     if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
414           ident_src->host, strlen (ident_src->host)) != 0)
415       return (-1);
416     SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
417   }
418
419   if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
420   {
421     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
422           ident_src->plugin, strlen (ident_src->plugin)) != 0)
423       return (-1);
424     SSTRNCPY (ident_dst->plugin, ident_src->plugin,
425         sizeof (ident_dst->plugin));
426   }
427
428   if (strcmp (ident_dst->plugin_instance,
429         ident_src->plugin_instance) != 0)
430   {
431     if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
432           ident_src->plugin_instance,
433           strlen (ident_src->plugin_instance)) != 0)
434       return (-1);
435     SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
436         sizeof (ident_dst->plugin_instance));
437   }
438
439   if (strcmp (ident_dst->type, ident_src->type) != 0)
440   {
441     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
442           ident_src->type, strlen (ident_src->type)) != 0)
443       return (-1);
444     SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
445   }
446
447   if (strcmp (ident_dst->type_instance,
448         ident_src->type_instance) != 0)
449   {
450     if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
451           ident_src->type_instance,
452           strlen (ident_src->type_instance)) != 0)
453       return (-1);
454     SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
455         sizeof (ident_dst->type_instance));
456   }
457
458   if (nb->state.time != vl->time)
459   {
460     if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
461       return (-1);
462     nb->state.time = vl->time;
463   }
464
465   if (nb->state.interval != vl->interval)
466   {
467     if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
468       return (-1);
469     nb->state.interval = vl->interval;
470   }
471
472   if (nb_add_values (&buffer, &buffer_size, vl) != 0)
473     return (-1);
474
475   nb->ptr = buffer;
476   nb->free = buffer_size;
477   return (0);
478 } /* }}} int nb_add_value_list */
479
480 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
481 {
482   char *buffer;
483   size_t buffer_size;
484
485   gcry_md_hd_t hd;
486   gcry_error_t err;
487   unsigned char *hash;
488   const size_t hash_length = 32;
489
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. */
493
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);
497
498   hd = NULL;
499   err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
500   if (err != 0)
501     return (-1);
502
503   assert (nb->password != NULL);
504   err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
505   if (err != 0)
506   {
507     gcry_md_close (hd);
508     return (-1);
509   }
510
511   gcry_md_write (hd, buffer, buffer_size);
512   hash = gcry_md_read (hd, GCRY_MD_SHA256);
513   if (hash == NULL)
514   {
515     gcry_md_close (hd);
516     return (-1);
517   }
518
519   assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
520   memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
521
522   gcry_md_close (hd);
523   return (0);
524 } /* }}} int nb_add_signature */
525
526 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
527 {
528   size_t package_length;
529   char *encr_ptr; /* pointer to data being encrypted */
530   size_t encr_size;
531
532   char *hash_ptr; /* pointer to data being hashed */
533   size_t hash_size;
534   char hash[20];
535
536   uint16_t pkg_length;
537   gcry_error_t err;
538
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));
543
544   /* Calculate what to hash */
545   hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
546   hash_size = package_length - nb->encr_header_len;
547
548   /* Calculate what to encrypt */
549   encr_ptr = hash_ptr - sizeof (hash);
550   encr_size = hash_size + sizeof (hash);
551
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));
555
556   if (nb->encr_cypher == NULL)
557   {
558     unsigned char password_hash[32];
559
560     err = gcry_cipher_open (&nb->encr_cypher,
561         GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
562     if (err != 0)
563       return (-1);
564
565     /* Calculate our 256bit key used for AES */
566     gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
567         nb->password, strlen (nb->password));
568
569     err = gcry_cipher_setkey (nb->encr_cypher,
570         password_hash, sizeof (password_hash));
571     if (err != 0)
572     {
573       gcry_cipher_close (nb->encr_cypher);
574       nb->encr_cypher = NULL;
575       return (-1);
576     }
577   }
578   else /* if (nb->encr_cypher != NULL) */
579   {
580     gcry_cipher_reset (nb->encr_cypher);
581   }
582
583   /* Set the initialization vector */
584   err = gcry_cipher_setiv (nb->encr_cypher,
585       nb->encr_iv, sizeof (nb->encr_iv));
586   if (err != 0)
587   {
588     gcry_cipher_close (nb->encr_cypher);
589     nb->encr_cypher = NULL;
590     return (-1);
591   }
592
593   /* Encrypt the buffer in-place */
594   err = gcry_cipher_encrypt (nb->encr_cypher,
595       encr_ptr, encr_size,
596       /* in = */ NULL, /* in len = */ 0);
597   if (err != 0)
598   {
599     gcry_cipher_close (nb->encr_cypher);
600     nb->encr_cypher = NULL;
601     return (-1);
602   }
603
604   return (0);
605 } /* }}} int nb_add_encryption */
606
607 /*
608  * Public functions
609  */
610 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
611 {
612   lcc_network_buffer_t *nb;
613
614   if (size == 0)
615     size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
616
617   if (size < 128)
618   {
619     errno = EINVAL;
620     return (NULL);
621   }
622
623   nb = malloc (sizeof (*nb));
624   if (nb == NULL)
625     return (NULL);
626   memset (nb, 0, sizeof (*nb));
627
628   nb->size = size;
629   nb->buffer = malloc (nb->size);
630   if (nb->buffer == NULL)
631   {
632     free (nb);
633     return (NULL);
634   }
635   memset (nb->buffer, 0, nb->size);
636
637   nb->ptr = nb->buffer;
638   nb->free = nb->size;
639
640   nb->seclevel = NONE;
641   nb->username = NULL;
642   nb->password = NULL;
643
644   return (nb);
645 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
646
647 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
648 {
649   if (nb == NULL)
650     return;
651
652   free (nb->buffer);
653   free (nb);
654 } /* }}} void lcc_network_buffer_destroy */
655
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)
659 {
660   char *username_copy;
661   char *password_copy;
662
663   if (level == NONE)
664   {
665     free (nb->username);
666     free (nb->password);
667     nb->username = NULL;
668     nb->password = NULL;
669     nb->seclevel = NONE;
670     lcc_network_buffer_initialize (nb);
671     return (0);
672   }
673
674   if (!have_gcrypt ())
675     return (ENOTSUP);
676
677   username_copy = strdup (username);
678   password_copy = strdup (password);
679   if ((username_copy == NULL) || (password_copy == NULL))
680   {
681     free (username_copy);
682     free (password_copy);
683     return (ENOMEM);
684   }
685
686   free (nb->username);
687   free (nb->password);
688   nb->username = username_copy;
689   nb->password = password_copy;
690   nb->seclevel = level;
691
692   lcc_network_buffer_initialize (nb);
693   return (0);
694 } /* }}} int lcc_network_buffer_set_security_level */
695
696 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
697 {
698   if (nb == NULL)
699     return (EINVAL);
700
701   memset (nb->buffer, 0, nb->size);
702   memset (&nb->state, 0, sizeof (nb->state));
703   nb->ptr = nb->buffer;
704   nb->free = nb->size;
705
706   if (nb->seclevel == SIGN)
707   {
708     size_t username_len;
709     uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
710     uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
711
712     assert (nb->username != NULL);
713     username_len = strlen (nb->username);
714     pkg_length = htons (pkg_length + ((uint16_t) username_len));
715
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;
721
722     memcpy (nb->ptr, nb->username, username_len);
723     nb->ptr += username_len;
724     nb->free -= username_len;
725   }
726   else if (nb->seclevel == ENCRYPT)
727   {
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);
732     char hash[20];
733
734     nb->encr_header_len = username_length;
735     nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
736
737     gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
738         GCRY_STRONG_RANDOM);
739
740     /* Filled in in finalize. */
741     memset (hash, 0, sizeof (hash));
742
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);
750   }
751
752   return (0);
753 } /* }}} int lcc_network_buffer_initialize */
754
755 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
756 {
757   if (nb == NULL)
758     return (EINVAL);
759
760   if (nb->seclevel == SIGN)
761     nb_add_signature (nb);
762   else if (nb->seclevel == ENCRYPT)
763     nb_add_encryption (nb);
764
765   return (0);
766 } /* }}} int lcc_network_buffer_finalize */
767
768 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
769     const lcc_value_list_t *vl)
770 {
771   int status;
772
773   if ((nb == NULL) || (vl == NULL))
774     return (EINVAL);
775
776   status = nb_add_value_list (nb, vl);
777   return (status);
778 } /* }}} int lcc_network_buffer_add_value */
779
780 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
781     void *buffer, size_t *buffer_size)
782 {
783   size_t sz_required;
784   size_t sz_available;
785
786   if ((nb == NULL) || (buffer_size == NULL))
787     return (EINVAL);
788
789   assert (nb->size >= nb->free);
790   sz_required = nb->size - nb->free;
791   sz_available = *buffer_size;
792
793   *buffer_size = sz_required;
794   if (buffer != NULL)
795     memcpy (buffer, nb->buffer,
796         (sz_available < sz_required) ? sz_available : sz_required);
797
798   return (0);
799 } /* }}} int lcc_network_buffer_get */
800
801 /* vim: set sw=2 sts=2 et fdm=marker : */