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