libyang  4.0.0
libyang is YANG data modelling language parser and toolkit written (and providing API) in C.
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
binary.c
Go to the documentation of this file.
1 
16 #define _GNU_SOURCE /* strdup */
17 
18 #include "plugins_types.h"
19 
20 #include <ctype.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "libyang.h"
26 
27 /* additional internal headers for some useful simple macros */
28 #include "compat.h"
29 #include "ly_common.h"
30 #include "plugins_internal.h" /* LY_TYPE_*_STR */
31 
44 static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
45 
46 static void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
47 static LY_ERR lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type,
48  struct lyd_value *storage, struct ly_err_item **err);
49 
62 static LY_ERR
63 binary_base64_encode(const struct ly_ctx *ctx, const char *data, uint32_t size, char **str, uint32_t *str_len)
64 {
65  uint32_t i;
66  char *ptr;
67 
68  *str_len = (size + 2) / 3 * 4;
69  *str = malloc(*str_len + 1);
70  LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
71  if (!(*str_len)) {
72  **str = 0;
73  return LY_SUCCESS;
74  }
75 
76  ptr = *str;
77  for (i = 0; i + 2 < size; i += 3) {
78  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
79  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
80  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
81  *ptr++ = b64_etable[data[i + 2] & 0x3F];
82  }
83  if (i < size) {
84  *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
85  if (i == (size - 1)) {
86  *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
87  *ptr++ = '=';
88  } else {
89  *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
90  *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
91  }
92  *ptr++ = '=';
93  }
94  *ptr = '\0';
95 
96  return LY_SUCCESS;
97 }
98 
102 static const int b64_dtable[256] = {
103  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
106  56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
107  7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
108  0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
109  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
110 };
111 
123 static LY_ERR
124 binary_base64_decode(const char *value, uint32_t value_size, void **data, uint32_t *size)
125 {
126  unsigned char *ptr = (unsigned char *)value;
127  uint32_t pad_chars, octet_count;
128  char *str;
129 
130  if (!value_size || (ptr[value_size - 1] != '=')) {
131  pad_chars = 0;
132  } else if (ptr[value_size - 2] == '=') {
133  pad_chars = 1;
134  } else {
135  pad_chars = 2;
136  }
137 
138  octet_count = ((value_size + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
139  *size = octet_count / 4 * 3 + pad_chars;
140 
141  str = malloc(*size + 1);
142  LY_CHECK_RET(!str, LY_EMEM);
143  str[*size] = '\0';
144 
145  for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
146  int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
147 
148  str[j++] = n >> 16;
149  str[j++] = n >> 8 & 0xFF;
150  str[j++] = n & 0xFF;
151  }
152  if (pad_chars) {
153  int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
154 
155  str[*size - pad_chars] = n >> 16;
156 
157  if (pad_chars == 2) {
158  n |= b64_dtable[ptr[octet_count + 2]] << 6;
159  n >>= 8 & 0xFF;
160  str[*size - pad_chars + 1] = n;
161  }
162  }
163 
164  *data = str;
165  return LY_SUCCESS;
166 }
167 
176 static LY_ERR
177 binary_base64_validate(const char *value, uint32_t value_size, struct ly_err_item **err)
178 {
179  uint32_t idx, pad;
180 
181  /* check correct characters in base64 */
182  idx = 0;
183  while ((idx < value_size) &&
184  ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
185  (('a' <= value[idx]) && (value[idx] <= 'z')) ||
186  (('0' <= value[idx]) && (value[idx] <= '9')) ||
187  ('+' == value[idx]) || ('/' == value[idx]))) {
188  idx++;
189  }
190 
191  /* find end of padding */
192  pad = 0;
193  while ((idx + pad < value_size) && (pad < 2) && (value[idx + pad] == '=')) {
194  pad++;
195  }
196 
197  /* check if value is valid base64 value */
198  if (value_size != idx + pad) {
199  if (isprint(value[idx + pad])) {
200  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
201  } else {
202  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
203  }
204  }
205 
206  if (value_size & 3) {
207  /* base64 length must be multiple of 4 chars */
208  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
209  }
210 
211  return LY_SUCCESS;
212 }
213 
223 static LY_ERR
224 binary_base64_newlines(char **value, uint32_t *value_size, uint32_t *options, struct ly_err_item **err)
225 {
226  char *val;
227  uint32_t size;
228 
229  if ((*value_size < 65) || ((*value)[64] != '\n')) {
230  /* no newlines */
231  return LY_SUCCESS;
232  }
233 
234  if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
235  /* make the value dynamic so we can modify it */
236  *value = strndup(*value, *value_size);
237  LY_CHECK_RET(!*value, LY_EMEM);
238  *options |= LYPLG_TYPE_STORE_DYNAMIC;
239  }
240 
241  val = *value;
242  size = *value_size;
243  while (size > 64) {
244  if (val[64] != '\n') {
245  /* missing, error */
246  return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
247  }
248 
249  /* remove the newline */
250  memmove(val + 64, val + 65, size - 64);
251  --(*value_size);
252  val += 64;
253  size -= 65;
254  }
255 
256  return LY_SUCCESS;
257 }
258 
259 static LY_ERR
260 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, uint32_t value_size_bits,
261  uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
262  const struct lysc_node *UNUSED(ctx_node), const struct lysc_ext_instance *UNUSED(top_ext),
263  struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres), struct ly_err_item **err)
264 {
265  LY_ERR ret = LY_SUCCESS;
266  struct lyd_value_binary *val;
267  uint32_t value_size;
268 
269  /* init storage */
270  memset(storage, 0, sizeof *storage);
271  LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
272  LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
273  storage->realtype = type;
274 
275  /* check value length */
276  ret = lyplg_type_check_value_size("binary", format, value_size_bits, LYPLG_LYB_SIZE_VARIABLE_BYTES, 0, &value_size,
277  err);
278  LY_CHECK_GOTO(ret, cleanup);
279 
280  if (format == LY_VALUE_LYB) {
281  /* store value */
282  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
283  val->data = (void *)value;
284  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
285  } else if (value_size) {
286  val->data = malloc(value_size);
287  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
288  memcpy(val->data, value, value_size);
289  } else {
290  val->data = strdup("");
291  LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
292  }
293 
294  /* store size */
295  val->size = value_size;
296 
297  /* success */
298  goto cleanup;
299  }
300 
301  /* check hints */
302  ret = lyplg_type_check_hints(hints, value, value_size, type->basetype, NULL, err);
303  LY_CHECK_GOTO(ret, cleanup);
304 
305  if (format != LY_VALUE_CANON) {
306  /* accept newline every 64 characters (PEM data) */
307  ret = binary_base64_newlines((char **)&value, &value_size, &options, err);
308  LY_CHECK_GOTO(ret, cleanup);
309 
310  /* validate */
311  ret = binary_base64_validate(value, value_size, err);
312  LY_CHECK_GOTO(ret, cleanup);
313  }
314 
315  /* get the binary value */
316  ret = binary_base64_decode(value, value_size, &val->data, &val->size);
317  LY_CHECK_GOTO(ret, cleanup);
318 
319  /* store canonical value */
320  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
321  ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
322  options &= ~LYPLG_TYPE_STORE_DYNAMIC;
323  LY_CHECK_GOTO(ret, cleanup);
324 
325  /* value may have been freed */
326  value = storage->_canonical;
327  } else {
328  ret = lydict_insert(ctx, value_size ? value : "", value_size, &storage->_canonical);
329  LY_CHECK_GOTO(ret, cleanup);
330  }
331 
332  if (!(options & LYPLG_TYPE_STORE_ONLY)) {
333  /* validate value */
334  ret = lyplg_type_validate_value_binary(ctx, type, storage, err);
335  LY_CHECK_GOTO(ret, cleanup);
336  }
337 
338 cleanup:
339  if (options & LYPLG_TYPE_STORE_DYNAMIC) {
340  free((void *)value);
341  }
342 
343  if (ret) {
344  lyplg_type_free_binary(ctx, storage);
345  }
346  return ret;
347 }
348 
352 static LY_ERR
353 lyplg_type_validate_value_binary(const struct ly_ctx *ctx, const struct lysc_type *type, struct lyd_value *storage,
354  struct ly_err_item **err)
355 {
356  struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
357  struct lyd_value_binary *val;
358  const void *value;
359  size_t value_len;
360 
361  LY_CHECK_ARG_RET(NULL, type, storage, err, LY_EINVAL);
362 
363  val = LYPLG_TYPE_VAL_IS_DYN(val) ? (struct lyd_value_binary *)(storage->dyn_mem) : (struct lyd_value_binary *)(storage->fixed_mem);
364  value = lyd_value_get_canonical(ctx, storage);
365  value_len = strlen(value);
366  *err = NULL;
367 
368  /* length restriction of the binary value */
369  if (type_bin->length) {
370  LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, val->size, value, value_len, err));
371  }
372 
373  return LY_SUCCESS;
374 }
375 
376 static LY_ERR
377 lyplg_type_compare_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
378 {
379  struct lyd_value_binary *v1, *v2;
380 
381  LYD_VALUE_GET(val1, v1);
382  LYD_VALUE_GET(val2, v2);
383 
384  if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
385  return LY_ENOT;
386  }
387  return LY_SUCCESS;
388 }
389 
390 static int
391 lyplg_type_sort_binary(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *val1, const struct lyd_value *val2)
392 {
393  struct lyd_value_binary *v1, *v2;
394  int cmp;
395 
396  LYD_VALUE_GET(val1, v1);
397  LYD_VALUE_GET(val2, v2);
398 
399  if (v1->size < v2->size) {
400  return -1;
401  } else if (v1->size > v2->size) {
402  return 1;
403  }
404 
405  cmp = memcmp(v1->data, v2->data, v1->size);
406 
407  return cmp;
408 }
409 
410 static const void *
411 lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
412  void *UNUSED(prefix_data), ly_bool *dynamic, uint32_t *value_size_bits)
413 {
414  struct lyd_value_binary *val;
415  char *ret;
416  uint32_t ret_size = 0;
417 
418  LYD_VALUE_GET(value, val);
419 
420  if (format == LY_VALUE_LYB) {
421  *dynamic = 0;
422  if (value_size_bits) {
423  *value_size_bits = val->size * 8;
424  }
425  return val->data;
426  }
427 
428  /* generate canonical value if not already */
429  if (!value->_canonical) {
430  /* get the base64 string value */
431  if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_size)) {
432  return NULL;
433  }
434 
435  /* store it */
436  if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
437  LOGMEM(ctx);
438  return NULL;
439  }
440  }
441 
442  /* use the cached canonical value */
443  if (dynamic) {
444  *dynamic = 0;
445  }
446  if (value_size_bits) {
447  *value_size_bits = ret_size ? ret_size * 8 : strlen(value->_canonical) * 8;
448  }
449  return value->_canonical;
450 }
451 
452 static LY_ERR
453 lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
454 {
455  LY_ERR ret;
456  struct lyd_value_binary *orig_val, *dup_val;
457 
458  memset(dup, 0, sizeof *dup);
459 
460  ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
461  LY_CHECK_GOTO(ret, error);
462 
463  LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
464  LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
465 
466  LYD_VALUE_GET(original, orig_val);
467 
468  dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
469  LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
470 
471  memcpy(dup_val->data, orig_val->data, orig_val->size);
472  dup_val->size = orig_val->size;
473  dup->realtype = original->realtype;
474 
475  return LY_SUCCESS;
476 
477 error:
478  lyplg_type_free_binary(ctx, dup);
479  return ret;
480 }
481 
482 static void
483 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
484 {
485  struct lyd_value_binary *val;
486 
487  lydict_remove(ctx, value->_canonical);
488  value->_canonical = NULL;
489  LYD_VALUE_GET(value, val);
490  if (val) {
491  free(val->data);
493  }
494 }
495 
504  {
505  .module = "",
506  .revision = NULL,
507  .name = LY_TYPE_BINARY_STR,
508 
509  .plugin.id = "ly2 binary",
510  .plugin.lyb_size = lyplg_type_lyb_size_variable_bytes,
511  .plugin.store = lyplg_type_store_binary,
512  .plugin.validate_value = lyplg_type_validate_value_binary,
513  .plugin.validate_tree = NULL,
514  .plugin.compare = lyplg_type_compare_binary,
515  .plugin.sort = lyplg_type_sort_binary,
516  .plugin.print = lyplg_type_print_binary,
517  .plugin.duplicate = lyplg_type_dup_binary,
518  .plugin.free = lyplg_type_free_binary,
519  },
520  {0}
521 };
struct lysc_type * realtype
Definition: tree_data.h:567
Compiled YANG data node.
Definition: tree_schema.h:1434
memset(value->fixed_mem, 0, LYD_VALUE_FIXED_MEM_SIZE)
LIBYANG_API_DECL LY_ERR lyplg_type_check_value_size(const char *type_name, LY_VALUE_FORMAT format, uint32_t value_size_bits, enum lyplg_lyb_size_type lyb_size_type, uint32_t lyb_fixed_size_bits, uint32_t *value_size, struct ly_err_item **err)
Check a value type in bits is correct and as expected.
YANG extension compiled instance.
Definition: plugins_exts.h:436
uint32_t size
Definition: tree_data.h:648
LY_ERR
libyang&#39;s error codes returned by the libyang functions.
Definition: log.h:240
uint8_t ly_bool
Type to indicate boolean value.
Definition: log.h:28
Definition: log.h:254
#define LYPLG_TYPE_STORE_DYNAMIC
#define LOGMEM(CTX)
Definition: tree_edit.h:22
int cmp
Definition: binary.c:394
Definition: log.h:244
LYPLG_TYPE_VAL_INLINE_DESTROY(val)
struct lysc_range * length
Definition: tree_schema.h:1423
Definition: log.h:242
The main libyang public header.
LIBYANG_API_DECL void lyplg_type_lyb_size_variable_bytes(const struct lysc_type *type, enum lyplg_lyb_size_type *size_type, uint32_t *fixed_size_bits)
Implementation of lyplg_type_lyb_size_clb for a type with variable length rounded to bytes...
YANG data representation.
Definition: tree_data.h:563
const char * _canonical
Definition: tree_data.h:564
Libyang full error structure.
Definition: log.h:285
LIBYANG_API_DECL const char * lyd_value_get_canonical(const struct ly_ctx *ctx, const struct lyd_value *value)
Get the (canonical) value of a lyd_value.
Definition: log.h:277
#define LYD_VALUE_GET(value, type_val)
Get the value in format specific to the type.
Definition: tree_data.h:606
LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *data_path, char *apptag, const char *err_format,...) _FORMAT_PRINTF(6
Create and fill error structure.
LIBYANG_API_DECL LY_ERR lydict_remove(const struct ly_ctx *ctx, const char *value)
Remove specified string from the dictionary. It decrement reference counter for the string and if it ...
LIBYANG_API_DECL LY_ERR lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
Insert string into dictionary. If the string is already present, only a reference counter is incremen...
struct lyplg_type_record plugins_binary[]
Plugin information for binray type implementation.
Definition: binary.c:503
Definition: log.h:248
const char * module
#define LYPLG_TYPE_VAL_IS_DYN(type_val)
Check whether specific type value needs to be allocated dynamically.
LIBYANG_API_DECL LY_ERR lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
Insert string into dictionary - zerocopy version. If the string is already present, only a reference counter is incremented and no memory allocation is performed. This insert function variant avoids duplication of specified value - it is inserted into the dictionary directly.
LY_VALUE_FORMAT
All kinds of supported value formats and prefix mappings to modules.
Definition: tree.h:234
#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val)
Prepare value memory for storing a specific type value, may be allocated dynamically.
LY_DATA_TYPE basetype
Definition: tree_schema.h:1300
return memcmp(v1->data, v2->data, bitmap_size)
LIBYANG_API_DECL LY_ERR lyplg_type_check_hints(uint32_t hints, const char *value, uint32_t value_len, LY_DATA_TYPE type, int *base, struct ly_err_item **err)
Check that the type is suitable for the parser&#39;s hints (if any) in the specified format.
API for (user) types plugins.
libyang context handler.
LIBYANG_API_DECL LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval, uint32_t strval_len, struct ly_err_item **err)
Data type validator for a range/length-restricted values.
Special lyd_value structure for built-in binary values.
Definition: tree_data.h:646
#define LYPLG_TYPE_STORE_ONLY