2 * collectd - src/libcollectdclient/network_buffer.c
3 * Copyright (C) 2010-2014 Florian octo Forster
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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.
24 * Florian octo Forster <octo at collectd.org>
44 #include <arpa/inet.h> /* htons */
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
55 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
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
61 # define GCRYPT_NO_DEPRECATED
63 # if defined __APPLE__
64 /* Re enable deprecation warnings */
65 # pragma GCC diagnostic warning "-Wdeprecated-declarations"
67 GCRY_THREAD_OPTION_PTHREAD_IMPL;
72 #include "collectd/network_buffer.h"
74 # include "collectd/win_hmac.h"
77 #define TYPE_HOST 0x0000
78 #define TYPE_TIME 0x0001
79 #define TYPE_TIME_HR 0x0008
80 #define TYPE_PLUGIN 0x0002
81 #define TYPE_PLUGIN_INSTANCE 0x0003
82 #define TYPE_TYPE 0x0004
83 #define TYPE_TYPE_INSTANCE 0x0005
84 #define TYPE_VALUES 0x0006
85 #define TYPE_INTERVAL 0x0007
86 #define TYPE_INTERVAL_HR 0x0009
88 /* Types to transmit notifications */
89 #define TYPE_MESSAGE 0x0100
90 #define TYPE_SEVERITY 0x0101
92 #define TYPE_SIGN_SHA256 0x0200
93 #define TYPE_ENCR_AES256 0x0210
95 #define PART_SIGNATURE_SHA256_SIZE 36
96 #define PART_ENCRYPTION_AES256_SIZE 42
102 #define ADD_GENERIC(nb,srcptr,size) do { \
103 assert ((size) <= (nb)->free); \
104 memcpy ((nb)->ptr, (srcptr), (size)); \
105 (nb)->ptr += (size); \
106 (nb)->free -= (size); \
109 #define ADD_STATIC(nb,var) \
110 ADD_GENERIC(nb,&(var),sizeof(var));
115 struct lcc_network_buffer_s
120 lcc_value_list_t state;
124 lcc_security_level_t seclevel;
129 gcry_cipher_hd_t encr_cypher;
130 size_t encr_header_len;
135 #define SSTRNCPY(dst,src,sz) do { \
136 strncpy ((dst), (src), (sz)); \
137 (dst)[(sz) - 1] = 0; \
143 static _Bool have_gcrypt (void) /* {{{ */
145 static _Bool result = 0;
146 static _Bool need_init = 1;
153 gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
155 if (!gcry_check_version (GCRYPT_VERSION))
158 gcry_control (GCRYCTL_INIT_SECMEM, 32768, 0);
159 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
166 } /* }}} _Bool have_gcrypt */
169 static uint64_t htonll (uint64_t val) /* {{{ */
171 static int config = 0;
179 uint16_t n = htons (h);
190 hi = (uint32_t) (val >> 32);
191 lo = (uint32_t) (val & 0x00000000FFFFFFFF);
196 return ((((uint64_t) lo) << 32) | ((uint64_t) hi));
197 } /* }}} uint64_t htonll */
200 static double htond (double val) /* {{{ */
202 static int config = 0;
204 union { uint8_t byte[8]; double floating; } in;
205 union { uint8_t byte[8]; double floating; } out;
209 double d = 8.642135e130;
214 if ((c[0] == 0x2f) && (c[1] == 0x25)
215 && (c[2] == 0xc0) && (c[3] == 0xc7)
216 && (c[4] == 0x43) && (c[5] == 0x2b)
217 && (c[6] == 0x1f) && (c[7] == 0x5b))
218 config = 1; /* need nothing */
219 else if ((c[7] == 0x2f) && (c[6] == 0x25)
220 && (c[5] == 0xc0) && (c[4] == 0xc7)
221 && (c[3] == 0x43) && (c[2] == 0x2b)
222 && (c[1] == 0x1f) && (c[0] == 0x5b))
223 config = 2; /* endian flip */
224 else if ((c[4] == 0x2f) && (c[5] == 0x25)
225 && (c[6] == 0xc0) && (c[7] == 0xc7)
226 && (c[0] == 0x43) && (c[1] == 0x2b)
227 && (c[2] == 0x1f) && (c[3] == 0x5b))
228 config = 3; /* int swap */
235 out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
236 out.byte[4] = out.byte[5] = 0x00;
239 return (out.floating);
241 else if (config == 1)
243 else if (config == 2)
246 out.byte[0] = in.byte[7];
247 out.byte[1] = in.byte[6];
248 out.byte[2] = in.byte[5];
249 out.byte[3] = in.byte[4];
250 out.byte[4] = in.byte[3];
251 out.byte[5] = in.byte[2];
252 out.byte[6] = in.byte[1];
253 out.byte[7] = in.byte[0];
254 return (out.floating);
256 else if (config == 3)
259 out.byte[0] = in.byte[4];
260 out.byte[1] = in.byte[5];
261 out.byte[2] = in.byte[6];
262 out.byte[3] = in.byte[7];
263 out.byte[4] = in.byte[0];
264 out.byte[5] = in.byte[1];
265 out.byte[6] = in.byte[2];
266 out.byte[7] = in.byte[3];
267 return (out.floating);
271 /* If in doubt, just copy the value back to the caller. */
274 } /* }}} double htond */
276 static int nb_add_values (char **ret_buffer, /* {{{ */
277 size_t *ret_buffer_len,
278 const lcc_value_list_t *vl)
285 uint16_t pkg_num_values;
286 uint8_t pkg_values_types[vl->values_len];
287 value_t pkg_values[vl->values_len];
292 packet_len = sizeof (pkg_type) + sizeof (pkg_length)
293 + sizeof (pkg_num_values)
294 + sizeof (pkg_values_types)
295 + sizeof (pkg_values);
297 if (*ret_buffer_len < packet_len)
300 pkg_type = htons (TYPE_VALUES);
301 pkg_length = htons ((uint16_t) packet_len);
302 pkg_num_values = htons ((uint16_t) vl->values_len);
304 for (i = 0; i < vl->values_len; i++)
306 pkg_values_types[i] = (uint8_t) vl->values_types[i];
307 switch (vl->values_types[i])
309 case LCC_TYPE_COUNTER:
310 pkg_values[i].counter = (counter_t) htonll (vl->values[i].counter);
314 pkg_values[i].gauge = (gauge_t) htond (vl->values[i].gauge);
317 case LCC_TYPE_DERIVE:
318 pkg_values[i].derive = (derive_t) htonll (vl->values[i].derive);
321 case LCC_TYPE_ABSOLUTE:
322 pkg_values[i].absolute = (absolute_t) htonll (vl->values[i].absolute);
327 } /* switch (vl->values_types[i]) */
328 } /* for (vl->values_len) */
331 * Use `memcpy' to write everything to the buffer, because the pointer
332 * may be unaligned and some architectures, such as SPARC, can't handle
335 packet_ptr = *ret_buffer;
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_num_values, sizeof (pkg_num_values));
342 offset += sizeof (pkg_num_values);
343 memcpy (packet_ptr + offset, pkg_values_types, sizeof (pkg_values_types));
344 offset += sizeof (pkg_values_types);
345 memcpy (packet_ptr + offset, pkg_values, sizeof (pkg_values));
346 offset += sizeof (pkg_values);
348 assert (offset == packet_len);
350 *ret_buffer = packet_ptr + packet_len;
351 *ret_buffer_len -= packet_len;
353 } /* }}} int nb_add_values */
355 static int nb_add_number (char **ret_buffer, /* {{{ */
356 size_t *ret_buffer_len,
357 uint16_t type, uint64_t value)
368 packet_len = sizeof (pkg_type)
369 + sizeof (pkg_length)
370 + sizeof (pkg_value);
372 if (*ret_buffer_len < packet_len)
375 pkg_type = htons (type);
376 pkg_length = htons ((uint16_t) packet_len);
377 pkg_value = htonll (value);
379 packet_ptr = *ret_buffer;
381 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
382 offset += sizeof (pkg_type);
383 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
384 offset += sizeof (pkg_length);
385 memcpy (packet_ptr + offset, &pkg_value, sizeof (pkg_value));
386 offset += sizeof (pkg_value);
388 assert (offset == packet_len);
390 *ret_buffer = packet_ptr + packet_len;
391 *ret_buffer_len -= packet_len;
393 } /* }}} int nb_add_number */
395 static int nb_add_time (char **ret_buffer, /* {{{ */
396 size_t *ret_buffer_len,
397 uint16_t type, double value)
399 /* Convert to collectd's "cdtime" representation. */
400 uint64_t cdtime_value = (uint64_t) (value * 1073741824.0);
401 return (nb_add_number (ret_buffer, ret_buffer_len, type, cdtime_value));
402 } /* }}} int nb_add_time */
404 static int nb_add_string (char **ret_buffer, /* {{{ */
405 size_t *ret_buffer_len,
406 uint16_t type, const char *str, size_t str_len)
416 packet_len = sizeof (pkg_type)
417 + sizeof (pkg_length)
419 if (*ret_buffer_len < packet_len)
422 pkg_type = htons (type);
423 pkg_length = htons ((uint16_t) packet_len);
425 packet_ptr = *ret_buffer;
427 memcpy (packet_ptr + offset, &pkg_type, sizeof (pkg_type));
428 offset += sizeof (pkg_type);
429 memcpy (packet_ptr + offset, &pkg_length, sizeof (pkg_length));
430 offset += sizeof (pkg_length);
431 memcpy (packet_ptr + offset, str, str_len);
433 memset (packet_ptr + offset, 0, 1);
436 assert (offset == packet_len);
438 *ret_buffer = packet_ptr + packet_len;
439 *ret_buffer_len -= packet_len;
441 } /* }}} int nb_add_string */
443 static int nb_add_value_list (lcc_network_buffer_t *nb, /* {{{ */
444 const lcc_value_list_t *vl)
446 char *buffer = nb->ptr;
447 size_t buffer_size = nb->free;
449 const lcc_identifier_t *ident_src;
450 lcc_identifier_t *ident_dst;
452 ident_src = &vl->identifier;
453 ident_dst = &nb->state.identifier;
455 if (strcmp (ident_dst->host, ident_src->host) != 0)
457 if (nb_add_string (&buffer, &buffer_size, TYPE_HOST,
458 ident_src->host, strlen (ident_src->host)) != 0)
460 SSTRNCPY (ident_dst->host, ident_src->host, sizeof (ident_dst->host));
463 if (strcmp (ident_dst->plugin, ident_src->plugin) != 0)
465 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN,
466 ident_src->plugin, strlen (ident_src->plugin)) != 0)
468 SSTRNCPY (ident_dst->plugin, ident_src->plugin,
469 sizeof (ident_dst->plugin));
472 if (strcmp (ident_dst->plugin_instance,
473 ident_src->plugin_instance) != 0)
475 if (nb_add_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
476 ident_src->plugin_instance,
477 strlen (ident_src->plugin_instance)) != 0)
479 SSTRNCPY (ident_dst->plugin_instance, ident_src->plugin_instance,
480 sizeof (ident_dst->plugin_instance));
483 if (strcmp (ident_dst->type, ident_src->type) != 0)
485 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE,
486 ident_src->type, strlen (ident_src->type)) != 0)
488 SSTRNCPY (ident_dst->type, ident_src->type, sizeof (ident_dst->type));
491 if (strcmp (ident_dst->type_instance,
492 ident_src->type_instance) != 0)
494 if (nb_add_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
495 ident_src->type_instance,
496 strlen (ident_src->type_instance)) != 0)
498 SSTRNCPY (ident_dst->type_instance, ident_src->type_instance,
499 sizeof (ident_dst->type_instance));
502 if (nb->state.time != vl->time)
504 if (nb_add_time (&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
506 nb->state.time = vl->time;
509 if (nb->state.interval != vl->interval)
511 if (nb_add_time (&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
513 nb->state.interval = vl->interval;
516 if (nb_add_values (&buffer, &buffer_size, vl) != 0)
520 nb->free = buffer_size;
522 } /* }}} int nb_add_value_list */
524 /* TODO: Add encryption for Windows */
526 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
531 BYTE hash[32] = { 0 };
532 DWORD hash_size = sizeof (hash) / sizeof (hash[0]);
539 /* The type, length and username have already been filled in by
540 * "lcc_network_buffer_initialize". All we do here is calculate the hash over
541 * the username and the data and add the hash value to the buffer. */
542 buffer = (LPBYTE) (nb->buffer + PART_SIGNATURE_SHA256_SIZE);
543 assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
544 buffer_size = (DWORD) (nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE));
546 status = CryptAcquireContext (&hProv,
547 /* szContainer = */ NULL,
548 /* CSP name = */ NULL,
549 /* provider type = */ PROV_RSA_AES,
550 /* flags = */ CRYPT_VERIFYCONTEXT);
554 status = CreateHMAC (hProv,
555 /* algorithm = */ CALG_SHA_256,
556 /* lpbKey = */ (LPBYTE) nb->password,
557 /* dwKeySize = */ (DWORD) strlen (nb->password),
559 /* lphHash = */ &hHash,
560 /* lphKey = */ &hKey);
563 CryptReleaseContext (hProv, /* dwFlags = */ 0);
567 status = CryptHashData (hHash,
568 /* pbData = */ buffer,
569 /* dwDataSize = */ buffer_size,
573 CryptDestroyHash (hHash);
574 CryptDestroyKey (hKey);
575 CryptReleaseContext (hProv, /* dwFlags = */ 0);
579 status = CryptGetHashParam (hHash,
580 /* dwParam = */ HP_HASHVAL,
582 /* pwdDataLen = */ &hash_size,
586 CryptDestroyHash (hHash);
587 CryptDestroyKey (hKey);
588 CryptReleaseContext (hProv, /* dwFlags = */ 0);
592 assert (((2 * sizeof (uint16_t)) + hash_size) == PART_SIGNATURE_SHA256_SIZE);
593 memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_size);
595 CryptDestroyHash (hHash);
596 CryptDestroyKey (hKey);
597 CryptReleaseContext (hProv, /* dwFlags = */ 0);
599 } /* }}} int nb_add_signature */
601 static int nb_add_signature (lcc_network_buffer_t *nb) /* {{{ */
609 const size_t hash_length = 32;
611 /* The type, length and username have already been filled in by
612 * "lcc_network_buffer_initialize". All we do here is calculate the hash over
613 * the username and the data and add the hash value to the buffer. */
615 buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
616 assert (nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
617 buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
620 err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
624 assert (nb->password != NULL);
625 err = gcry_md_setkey (hd, nb->password, strlen (nb->password));
632 gcry_md_write (hd, buffer, buffer_size);
633 hash = gcry_md_read (hd, GCRY_MD_SHA256);
640 assert (((2 * sizeof (uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
641 memcpy (nb->buffer + (2 * sizeof (uint16_t)), hash, hash_length);
645 } /* }}} int nb_add_signature */
647 static int nb_add_encryption (lcc_network_buffer_t *nb) /* {{{ */
649 size_t package_length;
650 char *encr_ptr; /* pointer to data being encrypted */
653 char *hash_ptr; /* pointer to data being hashed */
660 /* Fill in the package length */
661 package_length = nb->size - nb->free;
662 pkg_length = htons ((uint16_t) package_length);
663 memcpy (nb->buffer + 2, &pkg_length, sizeof (pkg_length));
665 /* Calculate what to hash */
666 hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
667 hash_size = package_length - nb->encr_header_len;
669 /* Calculate what to encrypt */
670 encr_ptr = hash_ptr - sizeof (hash);
671 encr_size = hash_size + sizeof (hash);
673 /* Calculate the SHA-1 hash */
674 gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash_ptr, hash_size);
675 memcpy (encr_ptr, hash, sizeof (hash));
677 if (nb->encr_cypher == NULL)
679 unsigned char password_hash[32];
681 err = gcry_cipher_open (&nb->encr_cypher,
682 GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
686 /* Calculate our 256bit key used for AES */
687 gcry_md_hash_buffer (GCRY_MD_SHA256, password_hash,
688 nb->password, strlen (nb->password));
690 err = gcry_cipher_setkey (nb->encr_cypher,
691 password_hash, sizeof (password_hash));
694 gcry_cipher_close (nb->encr_cypher);
695 nb->encr_cypher = NULL;
699 else /* if (nb->encr_cypher != NULL) */
701 gcry_cipher_reset (nb->encr_cypher);
704 /* Set the initialization vector */
705 err = gcry_cipher_setiv (nb->encr_cypher,
706 nb->encr_iv, sizeof (nb->encr_iv));
709 gcry_cipher_close (nb->encr_cypher);
710 nb->encr_cypher = NULL;
714 /* Encrypt the buffer in-place */
715 err = gcry_cipher_encrypt (nb->encr_cypher,
717 /* in = */ NULL, /* in len = */ 0);
720 gcry_cipher_close (nb->encr_cypher);
721 nb->encr_cypher = NULL;
726 } /* }}} int nb_add_encryption */
732 lcc_network_buffer_t *lcc_network_buffer_create (size_t size) /* {{{ */
734 lcc_network_buffer_t *nb;
737 size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
745 nb = malloc (sizeof (*nb));
748 memset (nb, 0, sizeof (*nb));
751 nb->buffer = malloc (nb->size);
752 if (nb->buffer == NULL)
757 memset (nb->buffer, 0, nb->size);
759 nb->ptr = nb->buffer;
767 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
769 void lcc_network_buffer_destroy (lcc_network_buffer_t *nb) /* {{{ */
776 } /* }}} void lcc_network_buffer_destroy */
778 int lcc_network_buffer_set_security_level (lcc_network_buffer_t *nb, /* {{{ */
779 lcc_security_level_t level,
780 const char *username, const char *password)
792 lcc_network_buffer_initialize (nb);
799 username_copy = strdup (username);
800 password_copy = strdup (password);
801 if ((username_copy == NULL) || (password_copy == NULL))
803 free (username_copy);
804 free (password_copy);
810 nb->username = username_copy;
811 nb->password = password_copy;
812 nb->seclevel = level;
814 lcc_network_buffer_initialize (nb);
816 } /* }}} int lcc_network_buffer_set_security_level */
818 int lcc_network_buffer_initialize (lcc_network_buffer_t *nb) /* {{{ */
823 memset (nb->buffer, 0, nb->size);
824 memset (&nb->state, 0, sizeof (nb->state));
825 nb->ptr = nb->buffer;
829 if (nb->seclevel == SIGN)
832 uint16_t pkg_type = htons (TYPE_SIGN_SHA256);
833 uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
835 assert (nb->username != NULL);
836 username_len = strlen (nb->username);
837 pkg_length = htons (pkg_length + ((uint16_t) username_len));
839 /* Fill in everything but the hash value here. */
840 memcpy (nb->ptr, &pkg_type, sizeof (pkg_type));
841 memcpy (nb->ptr + sizeof (pkg_type), &pkg_length, sizeof (pkg_length));
842 nb->ptr += PART_SIGNATURE_SHA256_SIZE;
843 nb->free -= PART_SIGNATURE_SHA256_SIZE;
845 memcpy (nb->ptr, nb->username, username_len);
846 nb->ptr += username_len;
847 nb->free -= username_len;
849 else if (nb->seclevel == ENCRYPT)
851 size_t username_length = strlen (nb->username);
852 uint16_t pkg_type = htons (TYPE_ENCR_AES256);
853 uint16_t pkg_length = 0; /* Filled in in finalize. */
854 uint16_t pkg_user_len = htons ((uint16_t) username_length);
857 nb->encr_header_len = username_length;
858 nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
860 gcry_randomize ((void *) &nb->encr_iv, sizeof (nb->encr_iv),
863 /* Filled in in finalize. */
864 memset (hash, 0, sizeof (hash));
866 ADD_STATIC (nb, pkg_type);
867 ADD_STATIC (nb, pkg_length);
868 ADD_STATIC (nb, pkg_user_len);
869 ADD_GENERIC (nb, nb->username, username_length);
870 ADD_GENERIC (nb, nb->encr_iv, sizeof (nb->encr_iv));
871 ADD_GENERIC (nb, hash, sizeof (hash));
872 assert ((nb->encr_header_len + nb->free) == nb->size);
877 } /* }}} int lcc_network_buffer_initialize */
879 int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */
885 if (nb->seclevel == SIGN)
886 nb_add_signature (nb);
888 if (nb->seclevel == SIGN)
889 nb_add_signature (nb);
890 else if (nb->seclevel == ENCRYPT)
891 nb_add_encryption (nb);
895 } /* }}} int lcc_network_buffer_finalize */
897 int lcc_network_buffer_add_value (lcc_network_buffer_t *nb, /* {{{ */
898 const lcc_value_list_t *vl)
902 if ((nb == NULL) || (vl == NULL))
905 status = nb_add_value_list (nb, vl);
907 } /* }}} int lcc_network_buffer_add_value */
909 int lcc_network_buffer_get (lcc_network_buffer_t *nb, /* {{{ */
910 void *buffer, size_t *buffer_size)
915 if ((nb == NULL) || (buffer_size == NULL))
918 assert (nb->size >= nb->free);
919 sz_required = nb->size - nb->free;
920 sz_available = *buffer_size;
922 *buffer_size = sz_required;
924 memcpy (buffer, nb->buffer,
925 (sz_available < sz_required) ? sz_available : sz_required);
928 } /* }}} int lcc_network_buffer_get */
930 /* vim: set sw=2 sts=2 et fdm=marker : */